Factorio-Access / FactorioAccess

An accessibility mod for the video game Factorio, making the game accessible to the blind and visually impaired.
Other
21 stars 9 forks source link

integrate (multiple) rulers with cursor skipping #242

Open ahicks92 opened 1 month ago

ahicks92 commented 1 month ago

I'm beginning to work with large train networks; this means taking blueprints of what are essentially irregular polygons; these have big voids around the edges. Without being able to "hit" something it's nearly impossible to get corners lined up for blueprints. In addition to the other possible uses I think this raises this part of the ruler plan in priority and afaik there's no ticket. This would just be adding a "should I stop" function to the rulers module with a simple algorithm that detects if this tile is a ruler and the last wasn't, but specifically not the opposite. Then rulers interrupt skipping but let the player skip along or off the side, e.g. moving past a ruler is just another skip.

LevFendi commented 1 month ago

This has been added in 6f64921. However, making apply generally will require keeping track of which ruler was touched last. Therefore this will be extended along with the extension to multiple ruler support.

ahicks92 commented 1 month ago

So obviously none of what I'm about to say matters today, but...

This is easier than that. Pseudocode:

now = get_num_rulers_aligned(pindex, start)
prev = now
for p in positions:
    now = get_num_rulers_aligned(pos)
    if now > prev then stop end

That allows skipping "down" the number of rulers under the cursor but doesn't allow skipping "up" the number of rulers, e.g. you can skip along one and will stop at any perpendicular.

In words it's a rising edge check on the number of rulers under the cursor. Should the cursor find a new one, it stops; should it leave one it keeps going.

If we are permitted to limit the number of rulers to 65535 (leaving 0 as a "nil"-type value) then it's easy to put the number of actual centers being touched by the cursor in the high 16 bits using the bit32 library. That naturally extends it without changing the above pseudocode by making ruler centers greater than any non-center tile. In other words with the above, plus this counter trick, the behaviors are as follows:

Technically with this algorithm if a player puts two rulers one horizontal tile apart we won't stop skipping if they skip from the center of one into the center of the other but I would argue either that this seems like good behavior because at minimum that doesn't seem like it has a use, and also that it'd be reasonable just to say "rulers must be x > 1 tiles apart".

Also note that without someone specifically implementing a quadtree or similar we won't be able to perform at 65k rulers anyway. Most spatial data structures have trouble with infinite-length lines and boxes.

If we don't want even that restriction the general form of this strategy is to have the ruler module expose an opaque position comparator taking the current and previous positions. The reason I was going to go there if I had done this myself is exactly this coming problem. With the module as it is now it's possible to build a comparable for two points that is able to tell us if the new point is "more ruler" for any number of rulers by taking the cardinality of the union of the sets of ruler ids and checking whether or not it is greater than either of the inputs. This can be done in O(rulers) time and efficiently in Lua if the author knows the Lua set trick where the keys are the members of the set and the values are just true true true. There are some utilities coming as part of new scanner which begin to make such set-based operations simpler, notably a helper which can convert an array to a set. Note however that in practice this isn't a total order; you can't sort by this comparator because the comparator's inputs would be order dependent: a op b != b op a if a and b (for example) have one ruler in common and one ruler each their own. The nice thing about a simpler count even with bit tricks is that such counts are a total order and obey the intuitive properties. Accounting for this possible mistake is unlikely to matter and can be done via naming in any case, since it's not too likely someone will want to sort somehow. Still it's what's in my head right now where I'm writing things not to be forgotten down.

In the general case I object to and try to avoid designing solutions which must remember history because it's reliant on order. Something that remembers history and isn't reliant on order is okay but meh, however histories that look at order often end up in repro messes either now or in the future when things become more complex. The above solution(s) should account for anything I can think of that we'd want to do to rulers without having to go there, and any repro involving it is limited to just needing the two coordinates of the tiles.