protectwise / troika

A JavaScript framework for interactive 3D and 2D visualizations
MIT License
1.62k stars 121 forks source link

Text: provide more accurate bounding box info #82

Closed asbjornlystrup closed 3 years ago

asbjornlystrup commented 4 years ago

Hi, a friend and I have spent a lot of time trying to figure out how to get the actual visual bounding box of the text. We need this so we can surround the text perfectly with a rectangular background.

We have tried with renderInfo.totalBounds and using the geometry's bounding box, but they're incorrect in the same way; glyphs with descenders (q, g, j, p etc.) don't affect the position of the bounding box, only the size. We tried setting various anchorX/anchorY values to align it properly, but the descenders are not taken into account. We also tried the renderInfo.glyphBounds, but the same holds for this.

I thought it might just be that this bbox is for highlighting text for selections, and to raycast against, but then it doesn't make sense that the descending glyphs change the size of the bbox, but not the position. We also tried getting the renderInfo.descender to add as an offset, but this holds the same value whether you have a decscending glyph like "p" or not. The same goes for renderInfo.ascender.

Moreover, there's a horizontal gap at the edge of the text as well, and this gap is bigger on the right side than on the left. We just need a perfect visually encapsulating bbox. Any ideas?

Apart from this, the asset has been working wonderfully. Thanks!

lojjic commented 4 years ago

The totalBounds includes extra padding for the SDFs so it's expected that it will be larger than the visible text. totalBlockSize is probably closer to what you want in terms of dimensions, but that doesn't include a starting x/y. Perhaps I should add something that is a full bounds rect wrapped to the visible portion.

I'm not quite following the part about how descenders "change the size of the bbox, but not the position", can you clarify that or attach some screenshots?

lojjic commented 4 years ago

Also, it would help to know more precisely what you're expecting when you say "the actual visual bounding box of the text".

Typically in HTML/CSS when you ask for bounds of a text element you get some extra space above and below the glyphs, depending on your line-height. The totalBlockSize I mentioned above would also include similar space. If you're wanting it to be tightly wrapped to the actual visual glyphs, that would be a slightly different calculation.

lineHeight: 1 image

lineHeight: 2 image

lojjic commented 4 years ago

I think I'm going to expose both the layout block bounds, ala CSS, and also the visible bounds of all the glyph paths. Depending on the particular font, the visible bounds may be smaller or larger than the layout bounds, and I can see both being useful in different circumstances.

Examples of the two sets of bounds - blue is the layout block bounds, red/purple is the visible bounds.

image image

Hopefully one of these, or a union of the two, is what you want?

asbjornlystrup commented 4 years ago

Thank you for the quick response. The red bbox is exactly what we need! Great :) Just so that we can specify our own padding around it and such.

asbjornlystrup commented 4 years ago

I'm not quite following the part about how descenders "change the size of the bbox, but not the position", can you clarify that or attach some screenshots?

This is what I meant; here the white outline is the "textRenderInfo.totalBounds" bbox (obtained in the sync-callback). Adding the p makes the bbox bigger, but doesn't change its position. anchorY is set to 'bottom' in both images. image image Another thing that would be nice is if there's a way to get the height of the descender of the text; the height that dips below the baseline of the text, so that you can manually align the text and such. In the two images, this descender height is the same value; -1.308. Shouldn't it be zero in the picture without the p?

lojjic commented 4 years ago

I definitely don't see that behavior; I'm wondering if maybe you're flipping the Y-axis values when rendering your rectangle, or something like that?

The descender is a metric defined by the font, so it's always nonzero. It also doesn't necessarily match the exact bottom of any particular glyph's visual descender. It's more of a tool for baseline alignment.

If there's a particular calculation you're wanting to do "so that you can manually align the text and such", let me know what that is; I'm guessing the metrics to do so are already present (or will be once I release these changes) but I want to be sure.

asbjornlystrup commented 4 years ago

Ah, I see, that makes sense.

I looked into it, and my mistake. Seems I forgot to add the totalBounds[0] and totalBounds[1] to the position, I just did width and height divided by two. Offsetting by this gives: image and image

My friend ran into the same issue, so I didn't think it would be that simple, but maybe he just made the same mistake.

The visual bbox will still be nice, but might not actually be necessary after all, as the images look pretty consistent now. I will ask him if he still needs it.

Thank you!

asbjornlystrup commented 4 years ago

Closing, as it seems like the mistake was on our side, and it's working after all. Again, thanks for the great work!

lojjic commented 4 years ago

Thanks for following up, I'm glad it wasn't quite as bad as you'd thought. :) However I do think the current metrics are less than ideal, and more precise metrics could be generally useful for others, so I'm going to reopen this as a reminder to improve it.

lojjic commented 3 years ago

See f3340ec1efac6a6b00f596d9ef898ed7c2a6568a

lojjic commented 3 years ago

The textRenderInfo now exposes blockBounds and visibleBounds, and the old totalBounds is deprecated. This is in v 0.34.0, just released.

@asbjornlystrup when you update to 0.34+, you'll need to update your code to use one of the new properties and adjust your padding.