[BUG][PLAYER] Zooming In Fails to Preserve Mouse-On-Land

Started by Simon, February 05, 2023, 03:01:43 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Simon

NL 12.12.5

The land are the physics pixels, i.e., the pixels where Lemmings physics apply. Zooming into the map will draw the land bigger, and we will see less of the land.

The canvas is the bitmap where we paint the land, then paint the lemmings, then paint the mouse cursor, ..., and finally paint the entire bitmap to the screen. After zooming in, we'll draw a land pixel as a square across several canvas pixels. The mouse position on the canvas stays the same throughout zooming in, that is good.

The mouse position on land will ideally remain the same before and after zooming in. When we're in small zoom levels and the land doesn't fill all of the canvas, preserving mouse-on-land won't always be possible. NL wants to fill the canvas with as much land as possible, and that's good.

The bug is now:
  • Zoom in until the land fills all of the canvas. Don't zoom in further yet, i.e., keep available at least one deeper zoom level for later.
  • Scroll to a position near the boundary of the land. Ideally, you're not at, but only near the boundary.
  • Put the mouse cursor anywhere except perfectly in the middle of the canvas.
  • Look at where the mouse points on the land.
  • Zoom in.
  • Look again at where the mouse points on the land. Now, it's a different pixel than where the mouse pointed on the land during step 4, before you zoomed in during step 5.
Expected instead: In step 6, mouse-on-land agrees with step 4's mouse-on-land.

Surprisingly, the bug doesn't happen when you're near the center of the map. There, NL computes and preserves mouse-on-land correctly when zooming in.

There are singular cases where the zooming preserves mouse-on-land even after back-and-forth zooming-in-and-out. Easiest example: Scroll to the perfect middle of the land. Put the mouse in the perfect middle of the canvas. Zoom in and out.

I haven't tested zooming out. That should ideally use the same code to preserve mouse-on-land, and fixing the bug for zooming in should ideally also apply to zooming out.

-- Simon

Simon

More research: Zooming-in never zooms onto the cursor unless the cursor is exactly in the middle of the canvas. Let's ignore this special case. (Reason for ignoring this: In general, any two linear maps agree at one point.)

Zooming-in seems to look at where the mouse is on the canvas, threating the position as a relative location of a point (mouse) on a rectangle (canvas), finding the corresponding relative location on the land (which doesn't even have the same aspect ratio), and zooming onto that point of the land.

As a result, when you don't move the mouse and repeatedly zoom-in, zoom-out, zoom-in, zoom-out, ..., you'll end up in one of the four corners of the level, depending on the quadrant of the canvas where your mouse was.

-- Simon

Simon

The zooming behavior is so aggravating that, several times now, I stopped playing NL before I wanted. And I don't understand how everybody else apparently doesn't care and happily plays on. Either everybody else is so thick not to notice any problem, or, more likely, subconciously avoids the nasty zooming even for precision work and just crawls into the screen really close.

Rule: Zooming must preserve mouse-on-land* and mouse-on-canvas.

This rule should apply even at the cost of not shifting all the land into view, i.e., the canvas should allow some land to remain out of view even when there is still void visible. Both Lix and NL fail this extended requirement. I should improve it in Lix.

Anyway, NL fails the rule even when the canvas is filled with land, and no void is visible. Please fix NL to follow the rule at least for when the canvas is filled entirely with land both before and after the zooming operation.

(*) There is a fun special case for the rule when the mouse points into the void, not onto the land. We should probably clamp the mouse-on-void coordinates then to get mouse-on-land coordinates, and preserve the clamped coordinates. This is hard to describe, and it's not necessary to understand yet because I'm mainly concerned with NL's zooming behavior when the entire canvas is filled with land, i.e., when no void is visible at all.




Edit 2023-05-06: I filed the extended requirement ("This rule should apply even at the cost of not shifting all alnd into view") for Lix: Zoom: Preserve Mouse-on-Land even when zoomed-out; don't screen-center (github #460)

-- Simon

WillLem

Yes, I see what you mean. The cursor jumps all over the place when zooming in. Ideally, it should be the "central" point when zooming. The same is true for the Editor, if I recall. More than happy to help debug this one, give me a shout if you're looking at it.

Dullstar

Quote from: Simon on February 07, 2023, 06:28:01 AM
I don't understand how everybody else apparently doesn't care and happily plays on. Either everybody else is so thick not to notice any problem, or, more likely, subconciously avoids the nasty zooming even for precision work and just crawls into the screen really close.

Honestly that could be it. I rarely use the zoom function, but weirdly, in Lix, I *will* use the zoom function way more often. Although in fairness Lix levels have a wider variety of starting zooms which could just make it feel like a more natural part of the engine. But it's also completely undeniable that the zoom function in Lix is much smoother than its NeoLemmix counterpart, and generally works exactly as I would expect for the most part until you zoom out too much relative to the size of the level (and idk, maybe that's the least bad solution at that point).

Dullstar

Another NeoLemmix zoom quirk, but this one might actually be intended: the panel will be located as close to the center of the screen as it will fit when the level's vertical size fits in its entirety, until you zoom in enough to force the panel all the way to the bottom. Thus, zooming in and out can cause the panel to move around.

Lix leaves the panel in place and anchors the bottom of the level to the panel. Personally, I would have expected a similar behavior as well. Still, it's probably worth asking around to see what behavior people expect.

WillLem

Quote from: Dullstar on March 05, 2023, 04:35:31 AM
Lix leaves the panel in place and anchors the bottom of the level to the panel. Personally, I would have expected a similar behavior as well. Still, it's probably worth asking around to see what behavior people expect.

It's possible they're part of the same problem, the more I think about it. Maybe anchoring the panel and/or level would actually fix it...?

Something to keep in mind is that we'd have to then decide whether to anchor the panel to the bottom of the screen or some arbitrary spot in the lower-middle. If the bottom, levels would be crunched to the bottom of the screen which would seem very wierd in Fullscreen at 1x zoom (even in Lix, this can appear a bit strange on smaller levels). The fact that panel & level move dynamically together as they do seems intended to me, given the much lower resolution of most levels in NeoLemmix.

namida

I can confirm that the panel moving with the game area is intentional. The other possibilities I considered were either anchoring everything to the bottom of the screen, or keeping the panel at the bottom while centering the level area; neither really felt right to me.

Re: the zoom matter, I recall several attempts to address this kind of issue in the past (although that might have been the editor), I'll have to take another look. I haven't yet. If no one's really been complaining about it being broken, I suspect no one will complain about fixing it either, but it'll go through an RC release either way.
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)

Simon

There is merit to the floating panel: It keeps the mousing range smaller.

Anchoring the panel to the bottom felt intuitively right, but I never thought about floating panel in Lix. Panel sits at bottom because it has always sat at the bottom. And I make the mousing range small by rendering the land directly over the panel.

Fitts's law says: Buttons at the bottom edge are easier to click because the mouse can't overshoot. But that holds only in Lix. NL doesn't trap the mouse. Wow, NL traps the mouse within the narrow corridor that has the buttons at the bottom. Never realized it. Nice!

I think I prefer the panel to stay in one place. But this preference might merely be from habit. Surprisingly, I'm not annoyed when the NL panel floats around. :D

If you anchor the panel, then whether to anchor the map to the panel depends on how thoroughly you want to preserve mouse-on-land. In Lix, forcing the rendered land to touch the panel felt right to me only because I minimize the amount of void on the canvas first, and only preserve mouse-on-land as a second aim. If I go all the way in Lix with preserving mouse-on-land even when the land doesn't fill the canvas, I'll have to allow the map to float around instead of anchoring the map to the panel.

-- Simon

WillLem

Quote from: Simon on March 06, 2023, 01:07:56 AM
NL doesn't trap the mouse. Wow, NL traps the mouse within the narrow corridor that has the buttons at the bottom. Never realized it. Nice!

There's also a hotkey to untrap it, which I'd suggest as an option for Lix. I'll post a topic for it though rather than discussing that here.

WillLem

This bug is also present in the Editor: zooming doesn't seem to anchor to any particular thing, be it scenery or cursor position.

I'd suggest that whatever fix we find for the Player should also be applied to the Editor.

Simon


procedure TGameWindow.ChangeZoom(aNewZoom: Integer; NoRedraw: Boolean = false);
var
  OSHorz, OSVert: Single;
  DoZoomOnCursor: Boolean;

  procedure SetCursorToCenter();
  var
    MousePos, ImgCenter: TPoint;
    ImgTopLeft, ImgBottomRight: TPoint;
  begin
    // Clip the Mouse position to the Image rectangle
    MousePos := Mouse.CursorPos;
    ImgTopLeft := Img.ClientToScreen(Point(0, 0));
    ImgBottomRight := Img.ClientToScreen(Point(Img.Width, Img.Height));
    MousePos.X := Max(Min(Mouse.CursorPos.X, ImgBottomRight.X), ImgTopLeft.X);
    MousePos.Y := Max(Min(Mouse.CursorPos.Y, ImgBottomRight.Y), ImgTopLeft.Y);
    // Get center of the image on the screen
    ImgCenter := Point(Trunc((ImgTopLeft.X + ImgBottomRight.X) / 2), Trunc((ImgTopLeft.Y + ImgBottomRight.Y) / 2));
    // Move the image location
    Img.OffsetHorz := Img.OffsetHorz - (MousePos.X - ImgCenter.X);
    Img.OffsetVert := Img.OffsetVert - (MousePos.Y - ImgCenter.Y);
  end;

  procedure ResetCenterToCursor();
  var
    MousePos, ImgCenter: TPoint;
    ImgTopLeft, ImgBottomRight: TPoint;
  begin
    // Clip the Mouse position to the Image rectangle
    MousePos := Mouse.CursorPos;
    ImgTopLeft := Img.ClientToScreen(Point(0, 0));
    ImgBottomRight := Img.ClientToScreen(Point(Img.Width, Img.Height));
    MousePos.X := Max(Min(Mouse.CursorPos.X, ImgBottomRight.X), ImgTopLeft.X);
    MousePos.Y := Max(Min(Mouse.CursorPos.Y, ImgBottomRight.Y), ImgTopLeft.Y);
    // Get center of the image on the screen
    ImgCenter := Point(Trunc((ImgTopLeft.X + ImgBottomRight.X) / 2), Trunc((ImgTopLeft.Y + ImgBottomRight.Y) / 2));
    // Move the image location
    Img.OffsetHorz := Img.OffsetHorz + (MousePos.X - ImgCenter.X);
    Img.OffsetVert := Img.OffsetVert + (MousePos.Y - ImgCenter.Y);
  end;

begin
  aNewZoom := Max(Min(fMaxZoom, aNewZoom), 1);
  if (aNewZoom = fInternalZoom) and not NoRedraw then
    Exit;

  DoZoomOnCursor := (aNewZoom > fInternalZoom);
  Img.BeginUpdate;
  SkillPanel.Image.BeginUpdate;
  try
    // If scrolling in, move the image to center on the cursor position.
    // We will ensure that this is a valid position later on.
    if DoZoomOnCursor then SetCursorToCenter;

    // Switch to top left coordinates, not the center of the image.
    OSHorz := Img.OffsetHorz - (Img.Width / 2);
    OSVert := Img.OffsetVert - (Img.Height / 2);
    OSHorz := (OSHorz * aNewZoom) / fInternalZoom;
    OSVert := (OSVert * aNewZoom) / fInternalZoom;

    Img.Scale := aNewZoom;

    fInternalZoom := aNewZoom;

    // Change the Img size and update everything accordingly.
    ApplyResize(true);

    //// If scrolling in, we wish to keep the pixel below the cursor constant.
    //// Therefore we have to move the current center back to the cursor position
    if DoZoomOnCursor then ResetCenterToCursor;

    // Move back to center coordinates.
    OSHorz := OSHorz + (Img.Width / 2);
    OSVert := OSVert + (Img.Height / 2);
    // Ensure that the offset doesn't move part of the visible area outside of the level area.
    Img.OffsetHorz := Min(Max(OSHorz, MinScroll), MaxScroll);
    Img.OffsetVert := Min(Max(OSVert, MinVScroll), MaxVScroll);

    SetRedraw(rdRedraw);
    CheckResetCursor(true);
  finally
    Img.EndUpdate;
    SkillPanel.Image.EndUpdate;
  end;
end;


WillLem found this code section and PM'd me. I wrote back; my replay is below the horizontal line here. The answer is from end of March 2023.




Hi,

I need more information about those names and will have to look at the full source repo. I won't have time this week [March 2023] for that. But if you have time, I have some ideas for promising attacks.

For a start, let's clarify:

What is "image":
The entire land, before rendering a section of it?
The section of the land that we are going to render?
The target bitmap onto which we draw during rendering?

What is "cursor position":
On the screen?
On the land? (I.e., it's what I call mouse-on-land? Have we really already recompute backwards from screen coordinates to land coordinates?)

Quote from: Will on April 02, 2023, 05:33:58 PM
// If scrolling in, move the image to center on the cursor position.
[...]
// Get center of the image on the screen
ImgCenter := Point(Trunc((ImgTopLeft.X + ImgBottomRight.X) / 2), Trunc((ImgTopLeft.Y +

Center? On first skimming, this looks like a dubious approach. Remember what we want to do: Preserve mouse-on-land before and after zooming. To do that, there is no obvious need to compute the center of anything.

... unless that's NL's preferred way of storing scrolling positions, because we need some reference coordinate for the scrolling position. Typical choices are indeed the center of the visible section of the land, or the top-left corner of the visible land. Does NL store the scrolling position via center of visible area or top-left of visible area? If it's center, fine. If it's top-left, we should be suspicious of computing the center in your shown source.

Quote//// If scrolling in, we wish to keep the pixel below the cursor constant.
//// Therefore we have to move the current center back to the cursor position

I assume "pixel below the cursor" means the land pixel.

Then I don't see why (move the current center back to the cursor position) accomplishes (keep the land pixel below the cursor constant).

Idea to investigate: What does the code assume to draw that conclusion? The distance on the land between mouse-on-land to center-of-rendering-area-on-land doesn't stay the same before and after zooming. Explicitly:

A = mouse on land before zooming
B = (center of rendering area) on land before zooming
C = mouse on land after zooming
D = (center of rendering area) on land after zooming

Then your qouted code looks like it wrongly assumes A − B = C − D.

From NL user experience, the code behaves worse the deeper we zoom in. Maybe (before zooming) in the wrong assumptions is really (at 1x zoom), even though I don't see how that would work in your quoted source.

Draw some examples on paper.

-- Simon

Simon

Trying to play contest levels, and of course the zoom bug rears its ugly head and kills the mood. Has namida or WillLem worked on this since? Or has feedback on the research?

I'm beginning to think that I should offer money for a fix. The problem is: For $200, I can buy a simple Windows laptop, install the build tools, and fix the bug myself. But anything less than $200 isn't going to convince namida to take time off his day job. :P

Although then the second-most annoying bug will be that the game exits on all lemmings lost. I thought we had that nailed years ago. And the third bug might be slippery RMB scrolling, which looked hard to fix without library calls to freeze the mouse on screen. Ramble ramble grumpy grumpy. Grumpy Simon!

-- Simon

WillLem

Quote from: Simon on August 24, 2023, 05:02:33 AM
Trying to play contest levels, and of course the zoom bug rears its ugly head and kills the mood. Has namida or WillLem worked on this since? Or has feedback on the research?

If you want to get on Mumble, I can screenshare my laptop and we can look at it together. I'm pretty fluent with the codebase now so I should be able to find things pretty quickly. If you know of a screensharing host that allows remote access as well, I'm happy for you to take control when necessary.

Quote from: Simon on August 24, 2023, 05:02:33 AM
Although then the second-most annoying bug will be that the game exits on all lemmings lost

SLX has fixed this one ;P

Simon

Happy to have a session if it helps you dig into the bug.

Mumble: I can make Saturday, 09:00 UTC. (This is in the morning, i.e., it's not 21:00 UTC.) Or I can make Sunday, 09:00 UTC.

I don't know about good screen sharing tools. My spontaneous idea was: On lixgame, I'll run a private RMTP server (like a twitch backend), e.g. Owncast, and you stream there with OBS (or similar screencasting software). The problem is that this would be a public livestreaming setup, optimized for chat, multiple viewers, audio etc., and not 1-to-1 screensharing.

-- Simon

WillLem

Quote from: Simon on August 24, 2023, 11:39:54 PM
Happy to have a session if it helps you dig into the bug.

Mumble: I can make Saturday, 09:00 UTC. (This is in the morning, i.e., it's not 21:00 UTC.) Or I can make Sunday, 09:00 UTC.

Can you perhaps do sometime in the evening during next week? I'll be busy pretty much all weekend and probably won't be able to find any time for programming.

The following weekend would be OK if weekday evenings are no good for you.

Regarding screen sharing, as long as you can see my screen and we can hear each other, that should be good enough. You can direct me as you wish (I should be fairly quick to respond as I'm much more fluent with Delphi/SLX codebase by now).

It's about time we got this one sorted! :thumbsup:

Simon

I'm on an odd rhythm: I sleep from late afternoon until around midnight. I have free time before work, and still start my day job before others appear online.

Catch me in Lix's IRC channel late at night. I can't guarantee that we'll find a good matching time, but if you have time, let's try. I'll sit in IRC around the clock, even when I'm working or sleeping; I won't reply instantly then.

I don't have screensharing ready yet. Normally no problem, I'll look at my own copy of the code, but the zoom bug in particular would enjoy screensharing.

-- Simon

namida

Given the recent decision to limit further work on NL, I'm not going to look at this for NL, however as there is active work on this for SLX, I've moved this topic to the SLX board rather than closing it.
My projects
2D Lemmings: NeoLemmix (engine) | Lemmings Plus Series (level packs) | Doomsday Lemmings (level pack)
3D Lemmings: Loap (engine) | L3DEdit (level / graphics editor) | L3DUtils (replay / etc utility) | Lemmings Plus 3D (level pack)
Non-Lemmings: Commander Keen: Galaxy Reimagined (a Commander Keen fangame)

Simon

New idea. We don't need to understand anything about the existing code. Instead:
  • Remember which land pixel is under mouse.
  • Zoom in/out with existing routine.
  • Look at which land pixel is under mouse.
  • Scroll map by difference of steps 1 and 3.
-- Simon

WillLem

Quote from: Simon on August 07, 2024, 06:42:02 PM
New idea. We don't need to understand anything about the existing code. Instead:
(Idea)

Great idea. Code already exists to fully support what you've proposed and shouldn't be too difficult to harness. The only issue might be unpicking the existing zoom thread (if necessary), depending on how far it's spread through the codebase.

WillLem

After some investigation/experimentation, we've ascertained the following points of interest which may lead to an eventual solution:

1) The existing variable for tracking the cursor position relative to the level is fRenderInterface.MousePos. This TPoint stays the same for each zoom factor, and updates as expected when moving the cursor across the level.

2) In order to use fRenderInterface.MousePos in GameWindow's ChangeZoom procedure, we need to initialize the Render Interface before the call to ChangeZoom in TGameWindow.PrepareGameParams. N.B. This may mean having to initialize it twice (i.e. keeping the existing initialization, which is after the call to ChangeZoom). So, this might cause problems elsewhere...

3) Mouse.CursorPos is the mouse position relative to the screen, not the level (or even the game window).

4) Img.OffsetHorz and Img.OffsetVert are most likely the variables needed for repositioning the level relative to the cursor after zooming (should we choose to go ahead with that method of fixing this bug), but this is not yet certain.

WillLem

Great news! I reached out to Anders Melander (Graphics32 maintainer) and asked if there might be an existing solution to this problem within the GR32 library.

Turns out there is!

GR32_Image has a procedure simply titled Zoom which can specify a Pivot point which anchors the image whilst zooming.

After a quick test, it seems to work a treat. The only issue I can find with it is that the cursor is repositioned when zooming between factor 1 and factor 2 (this is most pronounced at the extreme level edges/corners). Between factor 2 and higher, cursor position is preserved perfectly. I'm not sure exactly what's going on here, but it's definitely a promising start :thumbsup:

For clarity, in this context "factor" is defined by GameWindow's fInternalZoom field - this may not be quite the same value as the GR32 definition (actually, if the values are different for any reason, that could be the source of this bug).

@Simon - I've committed the new ChangeZoom procedure as provided by Anders. From here, we can likely find out how to fix 1-2 zooming. Once that's done, I'll create a PR for NeoLemmix.


WillLem

After some investigation, observation of Lix's zooming mechanics, and insight from Anders, it seems that the two display goals of "keep everything as visible as possible" and "zoom in to a very specific point under the mouse cursor" are mutually incompatible below a certain zoom factor, whatever the system's logic.

For example, even in Lix, the cursor jumps away from the edge when zooming in from the lowest possible zoom factor:


Lix - note that the cursor jumps away from both edges when zooming in from the very lowest zoom factor


And, the same behaviour in SuperLemmix

Anders observed that, at least in NL/SLX, this behaviour is most likely due to display logic that wants to display as much of the gameplay screen as possible at all times, with the zoom pivot only being "accepted" when the zoomed-in game display reaches a certain size (i.e. larger than the display window itself). This is unlikely to be something that we want to mess about with too much, so we must accept some loss of pivot when zooming in to/from the very lowest factor.

In practice, this actually isn't too much of an issue. Pinpoint zooming becomes more useful/relevant at higher zoom factors, where the image being displayed is larger than the display window and we want to get as close as possible to a particular lemming or level item. Both Lix and now SuperLemmix have acceptable levels of cursor re-adjustment through the very lowest zoom factors, whilst anchoring perfectly throughout the higher zoom factors:


Lix - note that it's necessary to readjust the cursor between the first few zoom factors


And, the same for SuperLemmix - only 1 adjustment is necessary because the game window is lower resolution than Lix's. However, on larger-resolution displays, more adjustments may be necessary before the anchor takes effect

Furthermore, both programs tend to display the level at a higher zoom factor than the lowest possible by default, so it's usually necessary to first decrease the zoom in order that it reaches a pivot-incompatible factor in the first place.

So, I'd suggest closing this as resolved unless anyone has any specific ideas. Although not actually intended as a final solution to the problem, Anders' fix has the zoom behaving much better than previously and is definitely a step in the right direction. It may of course need further testing to ensure stability, but from what I can see so far it seems perfectly usable. @namida It's a minimal change to the code, so rather than submit this as a PR, here's what's needed for NL if you're happy to merge the fix into 12.14. Simply replace the existing TGameWindow.ChangeZoom procedure with this one:

procedure TGameWindow.ChangeZoom(aNewZoom: Integer; NoRedraw: Boolean = false);
var
  Pivot: TPoint;
begin
  aNewZoom := Max(Min(fMaxZoom, aNewZoom), 1);
  if (aNewZoom = fInternalZoom) and not NoRedraw then
    Exit;

  SkillPanel.Image.BeginUpdate;
  try
    Pivot := Img.ScreenToClient(Mouse.CursorPos);
    Pivot := Img.ControlToBitmap(Pivot);
    Img.Zoom(aNewZoom, Pivot);

    fInternalZoom := aNewZoom;

    ApplyResize(False);

    SetRedraw(rdRedraw);
  finally
    SkillPanel.Image.EndUpdate;
  end;
end;

SuperLemmix implements this fix in Commit 1fe01cea0

Simon

QuoteLix
And, the same for SuperLemmix

Excellent work! And thanks for making these showcasing gifs!

Your screenshots show that, yes, when zooming between deep-enough zoom factors, mouse-on-land is now preserved. This fixes the main bug.

I'll happily test this in SLX. Let me know when you have an SLX executable for testing.

Quote"keep everything as visible as possible" and "zoom in to a very specific point under the mouse cursor" are mutually incompatible below a certain zoom factor, whatever the system's logic.

Correct.

The main bug is how NL's/SLX's zoom behave when the zoom factors both before and after fill the entire screen, i.e., on both levels, there is more level in both x- and y-direction than would fit onto the screen. This is the case in which I require that both mouse-on-screen and land-under-mouse are preserved.

QuoteI'd suggest closing this as resolved unless anyone has any specific ideas. Although not actually intended as a final solution to the problem, Anders' fix has the zoom behaving much better than previously and is definitely a step in the right direction.

Right, there is possibly more here.

geoo suggested to me in February 2023 for Lix: When the mouse is over the land (i.e., not over the void outside the land), I should still preserve land-under-mouse and mouse-on-screen. He's happy with sacrificing the goal of keeping as much as possible visible, i.e., preservation of land-under-mouse is more important than raising the screen's ratio of land-to-void.

I believe geoo is right here, but I haven't implented it in Lix yet. Lix's code for drawing the colored boundaries over the void is heavily tailored to rendering the land at the bottom center of the screen. geoo's idea will also procude new scrolling logic: You can have void showing at, e.g., the right, even though there is more unshown land to the left, and you can scroll left from here, but then not back to the right.

I'd say: Before you dig deeper into this idea (which is likely good, but not certainly), it's my turn again to improve/test this in Lix first.

See also my post from 2023-02-07 and Lix github #460: Preserve Mouse-on-Land even when zoomed-out.

-- Simon

Simon

I'll have time to test this weekend (Sat 9 to Sun 10). Please build it for me to test!

-- Simon