Log In  

Cart #jfowobora-1 | 2022-04-01 | Code ▽ | Embed ▽ | No License

I've been looking at how to draw circles that mimic the elegance of circ(). From the bbs that led me to
https://www.lexaloffle.com/bbs/?tid=29976
https://en.wikipedia.org/wiki/Talk:Midpoint_circle_algorithm

So I tested
Minsky/Midpoint/x,y=sin(),cos() against circ()
The verdict was the midpoint algorithm was the best alt method. But none of them r close to being as efficient as circ()..so I don't understand...how does circ() work? I especially don't understand cause the previous bbs link to the Minsky said it was superior to circ()...my tests say its not...but the posts r 5 years old so...has circ() been updated since maybe? Or what am I missing? All I did in my test is draw 100 circles and look at the values via ctrl+p...and the findings were explicit.

I'm really just looking at how to draw circles that look as good as circ()+efficiently+accuracy, since circ()/elipse() omit some of the custom arc-like shapes u might want to make or more custom circles like dotted etc that I need to use. You can of course, when able, for static circles just do the math calculations 1x outside the gameloop, then draw the coordinates via pset() in the gameloop...but many times you need dynamic circles running each frame looking clear as can be.

I'm also a bit upset by how circ() smooths its coordinates. The sin()/cos() method creates a much much more accurate circle hitbox. With circ() the collisions are always off, particularly in the right-side, when relying on it for the visual representation of where things are...and no you can't blame rounding error. I guess since I use sin()/cos() to make the imaginary hitboxes that would make sense that drawing the circles via sin()/cos() creates a more accurate representation. But u can't use midpoint circle algorithm, or whatever circ() is using, to make an efficient collision detection algorithm...is what I would presume?

P#109489 2022-04-01 01:29 ( Edited 2022-04-01 01:51)

first off, using what’s on screen for collision detection is usually discouraged (forces extremely simple visuals, can’t work off screen obviously, not able to handle tunneling…)
if you want collision based on a radius, maths in your code will be much better.

second, it is not possible to have a circ() routine in lua code as fast as the native function - see these functions as provided by the fantasy ‘hardware’ of the pico8 console.

as for drawing a circle, midpoint is the right choice - example: https://www.lexaloffle.com/bbs/?pid=49754#p

P#109496 2022-04-01 05:39 ( Edited 2022-04-01 05:54)

Can you explain what you mean by "whats on screen for collision detection is usually discouraged"? I was referring to bounding boxes style, I'm not familiar with other methods other than using pixel colour and flags so I might have mischaracterized what I meant. I would generally only run the method chosen for what's currently on the screen.

I'm not familiar with how 'native functions' of an engine
rly work compared to something I write within the program. Thinking of it as just being faster...okay but the performance difference is jarring to me. So the creator made a circ() function like the ones we are making...but compiling it as part of the hardware made it faster than anything we can create within the program? If that's true, would/could circ() be less or equally efficient to the functions we derived from midpoint/etc? I guess my Q is: am I chasing a supposed 'further optimization' that doesn't exist? Or is whats in the OP already 'better' than the code for circ(), it just runs slower cause its not within the software?

Can anyone confirm circ() runs on midpoint then? Could it run on something much worse and still be THAT fast compared to anything u make in the software? Another thread seemed to say, in passing, they suspected it runs on midpoint. I find assuming/guessing frustrating in this sense.

circ4() in the OP's cart also runs on midpoint and still performs better after removing the anti-aliasing stuff from the last entry in the linked ex. The error for the anti-aliasing seems to be integrated into the midpoint method, so I don't think you can do much there. If you wanted anti-aliasing I'm not sure if integrating with circ4() would be better or worse, but it would be better to use circ4() for normal circles and integrate aacirc with its anti-aliasing subparts to improve its efficacy.
circ4() .46 vs aacirc() .51
i.e.

function aacirc(x0,y0,r,c)
poke(0x5f25,c) --set color
 local x,y,dx,dy=flr(r),0,1,1
 r*=2
 local err=dx-r

 while x>=y do
  local dist=1+err/r
  pset(x0+x,y0+y)
  pset(x0+y,y0+x)
  pset(x0-y,y0+x)  
  pset(x0+x,y0-y)

  pset(x0-x,y0+y)  
  pset(x0-y,y0-x)
  pset(x0+y,y0-x)
  pset(x0-x,y0-y)

     if err<=0 then
   y+=1
   err+=dy
   dy+=2
        end
        if err>0 then
   x-=1
   dx+=2
   err+=dx-r
        end
    end
end
P#109526 2022-04-01 18:27 ( Edited 2022-04-01 18:37)

you cannot do a line() or circ() or map or ... function in lua code faster (or even close) than "native" pico8 functions.

P#109532 2022-04-01 21:14 ( Edited 2022-04-01 21:14)

circ() at top
minsky/circ4() midpoint and least $/ circ2() sincos w reflections/ the midpoint one above u linked to

They all look like crap compared to circ(), obviously its doing something the others aren't.

Here we have red circ2() sin/cos w reflections with green minsky overtop and white circ() overtop that.
You can see that problem how circ() skews to the right especially at the start with smaller sizes...which causes the hitbox problems when you do distance_between_centers<r1+r1 circle collision and use circ() to display the box onscreen. The accurate box is the circ2() sin/cos red circle, via the testing Minsky is atm 15% more efficient...circ4 the most efficient is only 20% better $ than sin/cos.

If you can't use circ() then:
-If u need small accurate circles displayed Minsky is best.
-circ4() midpoint is best cost everywhere but a bit off looking at smaller sizes.
-circ2() is the only accurate hitbox drawing tool, but visually its not smooth looking, and 20% more costly than circ4

function circ2(cx,cy,r,c,q)
poke(0x5f25,c) --set color
q=q or (r>15 and .005 or .01)
--r4:q4/r5:q3/r6:q2
--r>15 needs .5; r>40 needs .25 
    for i=0,.125,q do --0->.125 then reflects
        --.63/.62; .62/.61 if decimals
        local x,y=r*cos(i),r*sin(i)
        pset(cx+x,cy+y)
        pset(cx-y,cy-x)--miry=x .5
        pset(cx-y,cy+x)--mir .75
        pset(cx+y,cy-x)--mir .25
        pset(cx+y,cy+x)--mir .625
        pset(cx+x,cy-y) --x flip
        pset(cx-x,cy+y) --y flip
        pset(cx-x,cy-y) --xy flip
    end
end
P#109543 2022-04-01 22:57 ( Edited 2022-04-02 00:20)

not sure to understand what you are after.

if you want to display exact boundaries to, say, debug collisions then that's not going to work unless your game uses the pixel grid as world unit.

P#109565 2022-04-02 06:32

[Please log in to post a comment]