Coordinates for lines vs points (and pixel grid alignment for points)

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
grump
Party member
Posts: 947
Joined: Sat Jul 22, 2017 7:43 pm

Re: Coordinates for lines vs points (and pixel grid alignment for points)

Post by grump »

It's not that difficult or weird actually.

Rectangles and lines drawn at 0,0 will be blurry and should be placed at .5,.5 instead because that's how the coordinate system works.

Images and text drawn at .5,.5 will be blurry and should be placed at 0,0 instead because that's how the coordinate system works.

Meshes drawn at 0,0 will not be blurry. Meshes drawn at .5,.5 won't be blurry either, but will look a little bit weird. Unless they have a texture, then you gotta add .5. Or not. I can't remember. Because that's how the coordinate system works.

There are perfectly good explanations for why things are this way. It apparently all boils down to the fact that that's the way the coordinate system works and the best APIs are those with leaky abstractions.
User avatar
pgimeno
Party member
Posts: 3656
Joined: Sun Oct 18, 2015 2:58 pm

Re: Coordinates for lines vs points (and pixel grid alignment for points)

Post by pgimeno »

grump wrote: Mon May 03, 2021 12:39 pm It's not that difficult or weird actually.

Rectangles and lines drawn at 0,0 will be blurry and should be placed at .5,.5 instead because that's how the coordinate system works.

Images and text drawn at .5,.5 will be blurry and should be placed at 0,0 instead because that's how the coordinate system works.
Filled rectangles drawn at 0,0 will have no problem, no blurriness and no ambiguity hazards.

In my post above, when I talked about rectangles I meant filled rectangles. Rectangles and circles drawn in line mode use the line algorithm and are subject to the same treatment as lines. I forgot to specify that.

Anyway, stingund's concerns are not about blurring. The OP's purpose is to obtain crisp pixels, and the complaint is about what happens when you request "rough" line style, precisely in the situations where it would be blurry if you didn't. I'm trying to explain why the result is not clear-cut, why the unexpected result in the OP should actually be expected, and why the API is consistent (in fact, "too" consistent, in that rough mode lines are treated the same way as smooth mode lines, which is somewhat unexpected for someone like stingund who has expectations based on purely integer coordinate systems).

grump wrote: Mon May 03, 2021 12:39 pm Meshes drawn at 0,0 will not be blurry. Meshes drawn at .5,.5 won't be blurry either, but will look a little bit weird. Unless they have a texture, then you gotta add .5. Or not. I can't remember. Because that's how the coordinate system works.
My point here is that such a mesh may or may not cover the top left pixel row, depending on how the hardware rounds. Moreover, the texture's pixels, if drawn with filter "none", may similarly be rounded up or down, even within the texture, and cause distortion. I initially was among the people who thought that this problem was preventable and was therefore a bug, even reported it, and the answer left clear that it was a driver problem and that there was nothing Löve can do if it wants to take advantage of hardware acceleration.

grump wrote: Mon May 03, 2021 12:39 pm There are perfectly good explanations for why things are this way. It apparently all boils down to the fact that that's the way the coordinate system works and the best APIs are those with leaky abstractions.
Exactly that.
User avatar
Xii
Party member
Posts: 137
Joined: Thu Aug 13, 2020 9:09 pm
Contact:

Re: Coordinates for lines vs points (and pixel grid alignment for points)

Post by Xii »

stingund wrote: Mon May 03, 2021 12:43 am if you specify (2,2) for drawing a primitive of any type, be it a point, line or triangle, then pixel [2,2] in the surface should be unconditionally targetted.
There is no "pixel [2,2]". At coordinate [2,2] is the upper left corner of the square region covering [[2,2],[3,3]]. The exact center of this square is at [2.5,2.5]. There exists no integer index directly referring to this "pixel"; it is an area on a continuous surface.
stingund wrote: Mon May 03, 2021 12:43 am Love2D is a 2D platform, like pico-8. If you specify you want a point or a line at (3,3), that's where it should be. This is the API contract that should be presented to the user. And from reading the description of the various functions in the API, this is what seems to be implied.
LÖVE is not PICO-8. As love.graphics clearly indicates:
LÖVE's coordinate system is rooted in the upper-left corner of the screen, which is at location (0, 0) [...] the location (0, 0) aligns with the upper-left corner of the pixel as well, meaning that for some functions you may encounter off-by-one problems in the render output when drawing 1 pixel wide lines. You can try aligning the coordinate system with the center of pixels rather than their upper-left corner. Do this by passing x+0.5 and y+0.5
(emphasis mine)

PICO-8 is a retro environment, reflecting the days back when computers only did integer numbers. There was no 2.5, so pixels could be "referred to" by their integer coordinates. LÖVE is a modern environment. The canvas you draw on is a continuous 2-dimensional surface, its coordinates are floating point numbers with approximate, fractal precision. In this model, there is no 2! The exact number 2 might not actually exist! In Lua floating point, there are only three exact quantities: 0, 1, and NaN. All other numbers are approximations.

Lua does also have integers, where the exact number 2 does indeed exist. But if you peruse the LÖVE docs, you'll see that the drawing function arguments are typed as "number":
Number represents real (double-precision floating-point) numbers.
So there's no 2.
User avatar
pgimeno
Party member
Posts: 3656
Joined: Sun Oct 18, 2015 2:58 pm

Re: Coordinates for lines vs points (and pixel grid alignment for points)

Post by pgimeno »

Xii, I generally agree with your post, but this part:
Xii wrote: Mon May 03, 2021 4:36 pm The canvas you draw on is a continuous 2-dimensional surface, its coordinates are floating point numbers with approximate, fractal precision. In this model, there is no 2! The exact number 2 might not actually exist! In Lua floating point, there are only three exact quantities: 0, 1, and NaN. All other numbers are approximations.

Lua does also have integers, where the exact number 2 does indeed exist. But if you peruse the LÖVE docs, you'll see that the drawing function arguments are typed as "number":
Number represents real (double-precision floating-point) numbers.
So there's no 2.
is wrong.

Floating-point numbers can exactly represent many integers, and many fractions as long as the denominator of the simplified fraction is a power of two.

In particular, double-precision floating-point numbers can exactly represent all integers from -9007199254740992 to 9007199254740992 inclusive. The first integer they can't exactly represent is 9007199254740993 = 2^53 + 1.

Numbers like 0.2 = 1/5, however, can't be exactly represented, because the denominator is not a power of 2, and they need to be approximated. The closest approximation is the fraction 3602879701896397/18014398509481984 = 0.200000000000000011102230246251565404236316680908203125 and that's the actual number stored in a float when we tell it to store 0.2. In that sense, 0.2 does not "exist" as a double-precision floating-point number; but 2 does, because it can be exactly represented, and 0.200000000000000011102230246251565404236316680908203125 is also representable as a double-precision floating-point number. Other simpler examples: 0.75, 3.125; 0.8642578125.
User avatar
Xii
Party member
Posts: 137
Joined: Thu Aug 13, 2020 9:09 pm
Contact:

Re: Coordinates for lines vs points (and pixel grid alignment for points)

Post by Xii »

pgimeno wrote: Mon May 03, 2021 7:59 pm In particular, double-precision floating-point numbers can exactly represent all integers from -9007199254740992 to 9007199254740992 inclusive. The first integer they can't exactly represent is 9007199254740993 = 2^53 + 1.
Oh, ok. I've been taught to never assume floating point is exact, ever. :crazy:
User avatar
darkfrei
Party member
Posts: 1197
Joined: Sat Feb 08, 2020 11:09 pm

Re: Coordinates for lines vs points (and pixel grid alignment for points)

Post by darkfrei »

It's interesting, that Lua has no integer data type, but there is small different:

Code: Select all

a = 10 
b = 100/10 -- looks like 10
print(a) -- prints 10
print(b) -- prints 10.0
print(tostring(a==b)) -- prints true
Somehow 10.0 will be same as 10, but it prints it in other way.
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
stingund
Prole
Posts: 5
Joined: Tue Apr 13, 2021 5:18 am

Re: Coordinates for lines vs points (and pixel grid alignment for points)

Post by stingund »

4vZEROv wrote: Mon May 03, 2021 7:42 am
stingund wrote: Mon May 03, 2021 12:43 am In the end, it doesn't matter how the underlying hardware works.
Oh yes, yes it does x).

The best APIs are those that reflect how the hardware work, because they build knowledge of what exactly is going on physically.
See what Gump said, he understood exactly what I meant.

To be clear, this wasn't to say that how the underlying hardware doesn't matter to ANYONE, it absolutely matters to someone writing a cross-platform graphic API. But once an abstraction layer has been drawn, the design of the API should be such that you should not have to care how the underlying hardware works. Sure, sooner or later, you will have to debug an issue that requires that knowledge, but if the code you write above the abstraction layer frequently has to take underying details into account, then the cross-platform API is not proving useful.
For exemple, why do you think Vulkan is such a hit ?
As for Vulkan, I don't know what you consider a hit, but it is a fairly painful API to program against compared to Metal, Dx12, gnm (ps4), etc. And Vulkan, as other APIs, does its best to create hardware independent constructs and concepts (even though those are modelled after modern hardware) so that you, the application programmer, can write user code and shaders without having to do unique work per platform. In practice, you often have to specialize bits of code and shader code per platform, either for bugs or to get more performance. But compute and 3D graphics are complex things that eventually require specialization. Love2D is a 2D graphic API. We're not talking about the same level of complexity here. I hope we can agree on that!
Maybe this small point stuff seem pointless, but you will encounter a lot of much more bigger black boxes that you wish you could peek inside.
Having worked on consoles for a long time, I have had to debug black boxes frequently, and have had to take arcanely obscure platform differences into account many times (and I really like doing that). But if/when I design a cross-platform API, I put every effort into ensuring I can provide consistent behaviour across platforms.
Also if a behavior doesn't fit what you like, just program it !
Love2d source code is avaible here if you want to know what's going on: https://github.com/love2d/love
Well not really, there has to be agreement first. Otherwise any changes I would make would not be accepted. It's okay to discuss you know! :)

--

Having said this, I need to point out that the discussion about consistent behaviour I had with pgimeno was not meant to be about cross-platform development and whatnot, but rather about the results across different axis aligned primitive types. But it seems to have taken a turn that way. That's a bit off-topic!

Always great seeing people have enough passion to take the time to express their opinions in a thread. Not ignoring answers, just a bit busy atm to properly keep up.
Last edited by stingund on Tue May 04, 2021 8:37 am, edited 2 times in total.
stingund
Prole
Posts: 5
Joined: Tue Apr 13, 2021 5:18 am

Re: Coordinates for lines vs points (and pixel grid alignment for points)

Post by stingund »

LÖVE's coordinate system is rooted in the upper-left corner of the screen, which is at location (0, 0) [...] the location (0, 0) aligns with the upper-left corner of the pixel as well, meaning that for some functions you may encounter off-by-one problems in the render output when drawing 1 pixel wide lines. You can try aligning the coordinate system with the center of pixels rather than their upper-left corner. Do this by passing x+0.5 and y+0.5
Oh I hadn't seen this in the docs actually. Odd that I missed it, since this is the sort of information I was looking for.
In Lua floating point, there are only three exact quantities: 0, 1, and NaN. All other numbers are approximations.
You are underestimating the precision of floating point numbers by a lot! You have to get at much higher quantities before no longer being able to match integer.

It looks like this thread has gotten a lot of activity. I haven't been able to catch-up to everything, but it looks like I had missed some portions of the docs. I assumed a few things about the contract love2D was exposing, I'll familiarize myself with the docs better.
User avatar
pgimeno
Party member
Posts: 3656
Joined: Sun Oct 18, 2015 2:58 pm

Re: Coordinates for lines vs points (and pixel grid alignment for points)

Post by pgimeno »

darkfrei wrote: Tue May 04, 2021 7:05 am It's interesting, that Lua has no integer data type, but there is small different:

Code: Select all

a = 10 
b = 100/10 -- looks like 10
print(a) -- prints 10
print(b) -- prints 10.0
print(tostring(a==b)) -- prints true
Somehow 10.0 will be same as 10, but it prints it in other way.
Can't reproduce. I get 10 both times, not 10.0.

Edit: Ah, it happens in Lua 5.3, which has separate integer and float types. Löve only supports 5.1.
Last edited by pgimeno on Tue May 04, 2021 3:21 pm, edited 1 time in total.
User avatar
zorg
Party member
Posts: 3465
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Coordinates for lines vs points (and pixel grid alignment for points)

Post by zorg »

stingund wrote: Tue May 04, 2021 8:19 am ...But compute and 3D graphics are complex things that eventually require specialization. Love2D is a 2D graphic API. We're not talking about the same level of complexity here. I hope we can agree on that!
I mean, at its core, it uses OpenGL, and that's a 3D API... call it a simplification or a crutch that löve doesn't expose everything to do 3D stuff with, but it's something that's possible nevertheless.

To me, at least, there were posts in this thread that explained completely why the differences between drawing non-infinitessimally small rectangles that the APIs call points, non-infinitessimally thin rectangles the API calls lines and "regular" rectangles seemingly behave differently due to the assumed definitions of their positioning.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
Post Reply

Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 2 guests