ondras / rot.js

ROguelike Toolkit in JavaScript. Cool dungeon-related stuff, interactive manual, documentation, tests!
https://ondras.github.io/rot.js/hp/
BSD 3-Clause "New" or "Revised" License
2.34k stars 255 forks source link

display.drawText with hex layout #96

Open knurt opened 8 years ago

knurt commented 8 years ago

Short: Sometimes hexagonal backgrounds of text overlap:

Detailed explanation:

When I use hex layout, normally I want tiles to have a hexagonal background. This isn't an issue when neighbouring cells have space to the left and right.

When I want to write readable text with display.drawText, the characters should be close to each other. In the hex layout sometimes the issue arises that the backgrounds of the characters overlap.

Example:

<html>
<body></body>
<script src="https://raw.github.com/ondras/rot.js/master/rot.min.js"></script>
<script>

var display = new ROT.Display({width: 40, height: 20, layout: "hex"});

var heroName = "";

var handleCharacter = function(evt) {
    if (evt.charCode !== 0) {
        var char = String.fromCharCode(evt.charCode);
        heroName += char;
        display.drawText(2, 4, "> " + heroName);  // looks bad
    }
}

document.body.appendChild(display.getContainer());

display.drawText(2, 2, "What's your name?");  // looks good
display.drawText(2, 4, ">");

window.addEventListener("keypress", handleCharacter);

</script>
</html>

--- Want to back this issue? **[Post a bounty on it!](https://www.bountysource.com/issues/35700473-display-drawtext-with-hex-layout?utm_campaign=plugin&utm_content=tracker%2F297828&utm_medium=issues&utm_source=github)** We accept bounties via [Bountysource](https://www.bountysource.com/?utm_campaign=plugin&utm_content=tracker%2F297828&utm_medium=issues&utm_source=github).
knurt commented 8 years ago

It kind of works, when I use %b{transparent}. That's what I'm going to do for now. display.drawText(2, 4, "%b{transparent}> " + heroName);

The text gets slightly brighter and fatter, when I draw it over itself.

I could also write directly on the canvas. I would have to do some calculations, though, to translate the coordinates and change the alignment around. Maybe I could also write a function that writes two characters in every second tile.

ondras commented 8 years ago

Sounds like a reasonable bug. Will have a look into code to see how fixable this is.

@knurt, what behavior would you like to see? Note that hexes are indexed using the double width approach, so neighboring hexes differ by 2 (in X coord). This means that you either want closely packed characters or non-packed chars that match centers of those underlying hexes.

knurt commented 8 years ago

I think drawText(0,0,"01234") should draw letters at (0,0), (1,0), (2,0), (3,0) and (4,0), like it already does, as far as I understand. I think, that would be "non-packed chars that match centers of those underlying hexes", if you consider there to be a hex at (1,0). I mean: It should consider coordinates. Maybe that's what you meant by "closely packed". I think text shouldn't be written on every second coordinate, because that's less readable.

This way it would look more like an real terminal, a more consistent style. The disadvantage would be that if you have a spacing smaller or bigger than 1 this would make text look weird, but that is not related to the hex layout. If you were to disregard coordinates and just write the whole text with context.fillText(...), it would be readable, but maybe a break in style.

Of course, you know your code better and what it lends itself to. (I hope I englished that correctly ;)).

ondras commented 8 years ago

Hmm, I am afraid that your request does not make much sense then. The drawText function is basically a wrapper around the regular draw call, so individual characters are expected to have their own foreground/background colors (as you already figured out using the %c{}/%b{} syntax). This implies that individual characters shall be mapped to individual cells, which no longer holds, due to your (0,0), (1,0) requirement -- because individual cells are located at (0,0), (2,0) in this particular scenario.

Imagine drawing two regular hexes (red and blue) at (0,0) and (2,0); then drawing two letters (via drawText) with a green background color at (0,0). What is the expected outcome now? Can you sketch a sample image?

I would say that your use case is not compatible with the concept that "text is a set of individual character-based cells".

I see two approaches now:

1) adjusting the drawText for the hexagonal layout so that individual letters have double spacing, putting all letters inside hex centers (so "abc" would be placed at (0,0), (2,0), (4,0));

2) using some independent API to draw text atop hexes in a way that has no relation to hex sizing/positioning. This can be done as you suggested above (by hijacking the canvas and calling fillText manually), but is problematic with respect to position calculations and also the requestAnimationFrame loop that rot.js does internally for drawing).

knurt commented 8 years ago

I'm sorry if my request doesn't make sense. You are in no obligation! I'm honestly very grateful that you made this library at all!

Currently your example looks like this: overlap

I would like it to look like this: nooverlap

(This would also make sense: Every second letter has a transparent background.) variant

possible(?) approach 3: Draw all backgrounds first, then all foregrounds.

ondras commented 8 years ago

Hmm, interesting. The second image still looks a bit weird to me, but I see your point.

Alternative solution: if you do not require line wrapping, you can take advantage of rot.js's text centering feature:

display.draw("abcdef", 4, 0); // note this is not drawText

Does this work for your needs?

knurt commented 8 years ago

It kind of works. I would have to calculate a bit, if I want a certain left alignment and I would have to draw a background in a separate step, in case I want one (for example to remove old text).