What is wrong with this code?

Hi!

i have this code. I need to get the width of various labels in a table

		for k, v in pairs(self.table) do -- v = value, k = key
			if message[k]then
				print(message[k])
				label.set_text("#label", message[k])
				local metrics = label.get_text_metrics("#label")
				print(metrics.width)
				msg.post(v, "letter", {message[k], self.accumulatedwidth }) 
			end
		end

But even if the message is [ i , Q ], i still get

DEBUG:SCRIPT: i
DEBUG:SCRIPT: 102
DEBUG:SCRIPT: Q
DEBUG:SCRIPT: 102

(102 is the width of the text in the original label)

Okay.

here’s the current situation. The message in this case is {i,x,i,x}

		for k, v in pairs(message) do 
			label.set_text("#label", message[k]) --sets letter to pseudolabel
			local metrics = label.get_text_metrics("#label") -- gets width of pseudolabel
			self.accumulatedwidth = (self.accumulatedwidth + metrics.width) 
			print(message[k])
			print(metrics.width)
			print("accumulated width: " .. self.accumulatedwidth) 
			local currentrot = go.get(sender.path, "euler.z")
			go.set(self.widths[k], "euler.z", currentrot-(self.accumulatedwidth/7))
		end

prints

DEBUG:SCRIPT: i
DEBUG:SCRIPT: 39
DEBUG:SCRIPT: accumulated width: 39
DEBUG:SCRIPT: x
DEBUG:SCRIPT: 39
DEBUG:SCRIPT: accumulated width: 78
DEBUG:SCRIPT: i
DEBUG:SCRIPT: 39
DEBUG:SCRIPT: accumulated width: 117
DEBUG:SCRIPT: x
DEBUG:SCRIPT: 39
DEBUG:SCRIPT: accumulated width: 156

I do not understand why each metrics.width comes out at 39, when message[k] is different every time.

It looks like it’s a bug. (or at least not expected or documented…)

i was RIGHT! I am a genius!!

2 Likes

Yeah!! What do you say, do you want to take bets on how it goes next time something goes wrong? If it’s your bug or Defold’s? :innocent:

Must be using message passing under the hood?

Yes, as explained in the other post, the set_text is asynchronous, and the get_text_metrics isn’t.
Sometime (soon I hope), we’ll get resource properties, and we’ll be able to introduce a better/easier way to calculate metrics for a font.

I would definitely definitely bet on my code being at fault.

I wish this problem were documented in the literature. It’s actually the second time I’ve run into it.

Okay, next question: before I invent some hacked solution, I’d like to know if there’s an easy way to say “do this next frame”.

I always use https://github.com/britzl/ludobits/blob/master/ludobits/m/flow.lua by @britzl for things like waiting for x frames/seconds/next thing super useful.

5 Likes

nice! but i think i will just do something like

on action.pressed - set labels
on action.released - measure labels.

Using a timer with a 0 delay should work too, right?

would it also be possible to get the width of a label without actually setting that text? For example, something like:

label.get_text_metrics("#label", “iii”)

would tell you the pixels-width of the string “iii” if it were rendered using “#label” (without actually rendering it)

Well, yes, that’s the general idea. We’re not quite there yet, but it’d look like something like this:

local font = go.get("#label", "font") -- something like this (iirc)
local metrics = label.get_text_metrics(font, text)

It’s part of a feature we call “resource properties”, which is in the works, ETA unknown.

1 Like

Okay. There is something stranger happening here.

After getting some help on the slack channel, i decided to see what would happen if i stopped rotating the text to form a circle and just did the following code.

message is a table with a load of letters.
And, the message gets sent twice, to avoid the problem of not being able to get label metrics in the same frame as setting a label.

	elseif message_id == hash("text") then 
		print("------------       new message:       ------------" )
		self.accumulateddistance = 0
	for k, v in pairs(message) do 
		local mylabel = ("small object"..k.."#label")
		local myobj = ("/textcirclecollection/small object"..k)
		local metrics = label.get_text_metrics(mylabel) 
		self.accumulateddistance = self.accumulateddistance + metrics.width
		go.set(myobj, "position.x", (self.accumulateddistance))
		end
		--then we send the new letters
		for k, v in pairs(message) do 
			local mylabel = ("small object"..k.."#label")
			if message[k] == "-"then
				label.set_text(mylabel, "-")
				go.set(mylabel, "color.w", 0)
			else
				label.set_text(mylabel, message[k])
				go.set(mylabel, "color.w", 1)
			end
		end

The code appears to work well:
30

but has some unexpected problems with some letters. It’s weird.
10

Why is that?

okay… this is a bug.

Mattias in Slack:

Just a FYI: Asked the devs and they confirmed it is a bug. Probably because they count the tracking before adding the next glyph
width += (int16_t) (g->m_Advance + tracking);

I am not 100% sure this is the same issue. You are still using the same font? I can maybe take a look at it tomorrow to see if it is the same thing happening here.

I wouldn’t think that the “wi” shouldn’t behave like that if this was the same issue

1 Like

I agree with @Mattias_Hedberg , it doesn’t look like the same issue to me. Sure the metrics width for a text of a single character is off by the constant “tracking”, but that doesn’t explain why your “i” and “l” are displayed so close to each other, and the “d” is offset so much to the right. The “advance” number is the width of the glyph, and the tracking is based on the tracking you have on the label. The tracking defaults to 0.

I use wi because it is the most extreme example: w is the widest letter and i is the thinnest letter. The same problem has been happening since I started with this. Although I am sure my code is far from perfect, I find it hard to see what else may be the problem.

Would you mind sharing the test project with me?