TIC-80 icon indicating copy to clipboard operation
TIC-80 copied to clipboard

tri() not drawing lines correctly in 1.0

Open borbware opened this issue 2 years ago • 11 comments

Lines drawn with tri() aren't displayed correctly in 1.0. Either you don't see the line at all or it appears as dots here and there. 1.0: image 0.90: image

EDIT: Thought first I was drawing the lines with line() but i was actually using tri() instead.

borbware avatar May 02 '22 19:05 borbware

Lines draw more accurate now (so they might render slightly differently, but this certainly looks like a bug.

joshgoebel avatar May 02 '22 19:05 joshgoebel

@nesbox It also looks like the thickness of the lines can vary depending on the angle.. if that is a side effect of the accuracy changes I'd consider that "broken" personally. Each "step" should only draw a single pixel. IE a 46 degree stair step should look like this for large portions:

..O
.O.
O..

Not:

..OO
.OO.
OO..
O...

IE, the width shouldn't appear to vary because of the angle given. Right now it looks like the new algorithm can "double up" pixels in different cases...

joshgoebel avatar May 02 '22 20:05 joshgoebel

Right now it looks like the new algorithm can "double up" pixels in different cases

It's weird, the line() function can't "double up" pixels, because it makes only one pass on the long side, anyway we need exact line coordinates to reproduce and fix this.

nesbox avatar May 03 '22 06:05 nesbox

Oops, I was drawing the lines with tri() instead of line(). My bad!

Anyway, here's a short code that produces very different results on 1.0 and 0.90.

t=0
a=60
x=120
y=60

function TIC()
	cls(0)
	t=t+1
	tri(
		x,
		y,
		x+1,
		y+1,
		x+a*math.sin(t/10),
		y+a*math.cos(t/10),
		4
	)
end

image

borbware avatar May 03 '22 15:05 borbware

This is a new triangle rasterization algorithm, it draws pixels if their centers are inside the triangle. Sometimes it looks weird, but it's correct. Don't know, maybe use line() instead.

nesbox avatar May 04 '22 06:05 nesbox

I need tri() because i want to make the lines thicker on other end when the scanner is rotated, so using line() in this case is unfortunately out of the question.

I'm sure this new version of tri is more accurate and all, but for this it isn't very usable (and I'm sure this change in drawing algorithm will break many carts in tic80.com). Could it be possible to add the new version as a separate argument for tri, for instance as a boolean newAlgorithm=true or something like that? Or maybe the other way around, as oldAlgorithm=true at least.

I'd like to update to 1.0, but I can't just yet, at least for EMUUROM development.

borbware avatar May 04 '22 14:05 borbware

@borbware One idea: write your own fatLine that just calls line (with transformed end coordinates). IE, it would call line several times to get the effect you desire.

@nesbox I think if the triangles edges aren't complete it's really hard to argue it's "correct" in any meaningful way... unless we just want to officially document "triangle only works if the length of all sides are greater than 5 pixels" or some such. Is that a new requirement we want to enforce? It seems kind of ugly to need to state that.

I'd argue this is a bug in the fill algorithm? IE, if our fill array looks like this:

y:20 x:12-15 
// pixels 16, 17, 18, and 19 are unrendered
y:21 x:20-25

It's clear that is a 5 pixel gap in the render and we should extend the prior segment (or the current segment) to cover the difference... wouldn't this be a fairly easy fix? IE, the expectation should be that the ranges will always touch... I believe this would require one/two extra conditionals per fill line.

Correct:

y:20 x:12-15 
y:21 x:16-25 // rewound to 16 to touch 15 from the previous segment

You willing to consider a fix along these lines?

joshgoebel avatar May 04 '22 18:05 joshgoebel

Another solution is to use tri() together with trib()

function _tri(x0,y0,x1,y1,x2,y2,c)
  tri(x0,y0,x1,y1,x2,y2,c)
  trib(x0,y0,x1,y1,x2,y2,c)
end

screen17

nesbox avatar May 05 '22 06:05 nesbox

@borbware Since you seem to know the math here could you provide a small "clock" example with just one hand spinning in a slow circle? IE, something that will show the issue from a lot of different angles so if I find a fix it's easy to confirm it works in all cases.

If you do I'll take a look at this myself and see if there seems to be a simple solution or not.

joshgoebel avatar May 05 '22 17:05 joshgoebel

@joshgoebel here ya go: a clock example with tri() drawn on top of trib().

I think it would make sense that tri() would draw the exact same shape as trib, but just the middle parts coloured - at least in 1.0 tri() seems to draw a smaller triangle.

t=0
a=60
b=20
x=100
y=60

function draw(x,y,c,func)
 func(
  x,
  y,
  x+b*math.sin(t/100+math.pi/8),
  y+b*math.cos(t/100+math.pi/8),
  x+a*math.sin(t/100),
  y+a*math.cos(t/100),
  c
 )
end

function TIC()
 cls(0)
 t=t+1
 draw(x,y,4,trib)
 draw(x,y,7,tri)
end

borbware avatar May 10 '22 19:05 borbware

I don't know how to find a suitable solution for this situation, the new triangle rasterization algorithm works great with texturing and I don't want to give it up. As a solution, I suggest calling tri() and trib() sequentially to draw the filled triangle as before.

nesbox avatar Feb 05 '23 12:02 nesbox