Log In  

Cart #56846 | 2018-09-18 | Code ▽ | Embed ▽ | No License
4

As I needed an OVAL drawing routine for the paint cart I'm working on, I was surprised to see how small of code it was required in order to do such an effective one !

Here is the original C source code by roger dahl found in stackoverflow.com :

for(int y=-radius; y<=radius; y++)
    for(int x=-radius; x<=radius; x++)
        if(x*x+y*y <= radius*radius)
            setpixel(origin.x+x, origin.y+y);

Here is my code for Pico-8:

-- lovely oval drawing function
-- converted by dw817
-- original by roger dahl

function oval(h,v,x,y,c)
  x=(x+1)/2 y=(y+1)/2 h-=1-x v-=1-y
  for i=-y,y do
    for j=-x,x do
      if ((j/x)^2+(i/y)^2<1) pset(h+j,v+i,c)
    end
  end
end

x1=64-30
y1=64-20
x2=60
y2=40

cls()
rect(x1,y1,x1+x2-1,y1+y2-1,1)
oval(x1,y1,x2,y2,12)

As you can see it's a real plotting monster, but it DOES work, and I checked, it does not draw over any pixels it previously drew, so it's going about as fast as it can I think.

P#56847 2018-09-18 12:03 ( Edited 2018-09-20 01:21)

On PICO-8 you could engage the power of the pseudo-GPU by using rectfill() to draw a symmetrical span on the current line as soon as you get to a pixel that's "inside" the oval.

By the way, a technical heads-up: the ^ power operator actually calls a hidden pow() function, even for simple powers like 2, so it (annoyingly) takes a fair bit of time. Thus, it'd probably be faster to do j*j/x/x + i*i/y/y.

Try this:

function oval(h,v,x,y,c)
  x=(x+1)/2 y=(y+1)/2 h-=1-x v-=1-y
  for i=-y,y do
    for j=-x,0 do
      if j*j/x/x+i*i/y/y<1 then
        rectfill(h+j,v+i,h-j,v+i,c)
        break
      end
    end
  end
end

You could also do something where you only iterate i=-y,0 and then draw the same span above and below the center point, but you'd want to make sure you don't draw the middle span twice.

Interesting method. I think for large circles the number of false hits outside the circle might be a little overpowering vs. the math for calculating the extents of the span based on y, but for small ones it might be better just to test and discard like this.

P#56862 2018-09-18 16:08 ( Edited 2018-09-18 20:16)
1

Felice strikes again !

I would really learn to hate you from sheer jealousy for being so darned smart and right all the time. 🙄

As the fact is I desperately need your tutelage and methodology as it and often has been superior to mine, and I thank you for that as I am always learning something new.

Now I =WAS= earlier thinking of using LINE() to speed up the oval drawing but wasn't really sure how except by waiting for I to not equal the last I and then record coordinates left and right from the last plot that didn't plot.

That would work, but yours is spot on.

Clearly you have a boffo way of doing it here.

And - I'd like to use this. Unfortunately I can't. Not with my current project.

The paint cart I'm working on - I want it to have a really robust UNDO and REDO feature up to 128 steps in the paint program. And because of this every element, line, circle, oval, rectangle, filled and otherwise, user sprites plotted and maybe no longer even loaded - must all use "plot a single pixel" method so that same pixel can later be unplotted from a table for UNDO or REDO.

I'm also checking to make sure that every single pixel plotted is not the same color as the one beneath. Using true lines or rectangles would not check for that and make errors in undo attempts.

Now make no mistake, your filled oval routine is awesome and wicked fast. It would be of great benefit for anyone doing vector graphics, like something Penguinsoft did years ago.

Just ... not for my current project. Watch out though. I'll likely find a use for it after this one. :)

I will look at the power ^ operand I am using though and see if I can improve upon it as you said it was slow.

P#56865 2018-09-18 17:16 ( Edited 2018-09-18 21:19)
1

You guys are like ascetic monks on a quest for algorithmic perfection... I love it (even though I'm following a very different and clumsier path ;-)

P#56877 2018-09-19 02:28 ( Edited 2018-09-19 06:28)

How exactly do you plan to maintain an undo buffer at pixel level????
A in-memory cube of 128x128x128??

Given that you have only math shapes, why not simply record the commands that were entered by the user? (never heard of ‘Design Patterns’ by Erich Gamma?).
That will cost you a redraw of the whole screen (but, hey, if you can’t draw the scene you probably have already blown up pico limits!).

P#56886 2018-09-19 15:10 ( Edited 2018-09-19 19:11)

yeah i'd do what @freds72 suggests:

have a screen buffer store the screen at the furthest 'undo' point (use part of gfx and map for this) and just record the steps used to get from there to the present state as opcode-style bytes that are stepped through as needed*.

*potentially every frame. but you can get away with less often if you also buffer the current state of the screen before drawing your cursor and such on top.

P#56890 2018-09-19 15:39 ( Edited 2018-09-19 19:39)

Probably the most token-light way to do it, while still ram-light as well, would be to run-length-encode an XOR diff between the current screen and the previous screen into a string. This is specific to PICO-8 because the screen is small and the bit depth is minimal.

You could also constrain it to an affected rectangle, but just doing the whole screen might be fine, depending on how well your RLE works for large swathes of zeroes.

Nice thing about XOR is that it makes undo/redo very easy to implement, since it makes them symmetrical.

P#56902 2018-09-19 19:20 ( Edited 2018-09-19 23:26)

Actually I was incredibly and pleasantly pleased to find I can record well past 128 full-screens of data, provided they are strings. I watched memory decrease as well. Used up only half of it to do that.

More than enough, and in any case I'm not undoing entire 128x128 areas, no just "objects" that are drawn, like Ovals, etc. Here ... is a sample to show you.

Cart #56905 | 2018-09-20 | Code ▽ | Embed ▽ | No License

Run it, 128 ovals are made. Press (Z) and an UNDO step is made, all the way back to the beginning. Understand I am not redrawing any ovals at all. I am drawing the contents of the pixels BEFORE the oval overwrites it. Then I just play in reverse the pixel set that was stored.

The number at the top is the amount of memory being taken. Not nearly as much as I thought it would.

And yes, I was talking to my Dr today about XOR, how really that Apple tank game I made was supposed to use XOR for each dot like Bill Budge did originally - instead I opted to have true graphic sprites.

Those of you who are not familiar, plotting XOR for PICO is where you take 15 and subtract the color # of the pixel you are plotting on.

If it was color #0 you get color #15, color #1 yields color #14 and so on. The real advantage is =IF= you plot the exact same thing 15-(current color of pixel) it RETURNS the original color back prior to the first XPLOT. A nifty way to draw a "ghost" image so you can see the area it takes without damaging the background at all or requiring an UNDO storage initially.

And no, my paint program will not just have shapes. You have lines, you can outline images, import sprites, onboard proportional text, system 3x5 test, onboard 8x8 font, onboard 8x8 stamps and you can manually import a full 128x128 custom sprite area to draw from as that area is perfectly blank and I'm not using any of it.

[56x8]

(import [image].png) All of this area can also be used as stamps for your page work.

Pages (and regions) are saved as either a clipboard source-code insert to your program or as a BBS copy to clipboard image for pasting directly to the forum.

Will work with 8-full 128x128x16 screens at a time. Undo will jump to the page you last drew on when you invoke it.

Neat stuff ! Likely I will do the XPLOT when people are selecting ovals and rectangles and stuff and only when they press (Z) to confirm drawing the element, it will go to draw it proper - once again saving it to the UNDO buffer first so anything drawn, vector, line, object, area, stamp, sprite, tiles, can always be undone.

Currently I am using 80.0488 memory in my main program which already includes the 128x128 system image (proportional text, custom 8x8 font, system icons and devices), 128x128 16-color logo, and multiple pixel and location lookup tables and arrays.

There's enough memory for it all. :)

P#56906 2018-09-19 20:54 ( Edited 2018-09-20 01:09)

Why were you talking to your doctor about that?

P#56907 2018-09-19 21:14 ( Edited 2018-09-20 01:14)

He's my psych. I see him every week. He says tell me what you're doing every day, and he writes it down - and so well, we talk just about everything that I wind up doing, as of recent, chapters to my 3rd book and working on this paint program for PICO. :)

I had hoped I would finish writing it by the end of this month but looks like it's going to be October.

P#56908 2018-09-19 21:21 ( Edited 2018-09-20 01:21)

@Felice what is v and h in your ovel filling algorithm? It's offtopic but I was wondering because I am trying to make a nes like game engine in C#

P#110304 2022-04-16 04:58

Erno, I truly believe no-one is a clumsier programmer than me. No, I'm the worst. I'm not only a bad programmer, I refuse to grow into object-oriented methods and coding - instead insisting on old-school programming effective at times though it may be.

Much good it will do me in the future I think but this is not all I do.

I like your analogy though. Algorithmic perfection. Has a nice ring to it. :)

BTW, ZEP has created 2-new commands that defeat our exercise here:

OVAL(X0, Y0, X1, Y1, [COL])
OVALFILL(X0, Y0, X1, Y1, [COL])
P#110958 2022-04-27 18:06 ( Edited 2022-04-27 18:44)

[Please log in to post a comment]

Follow Lexaloffle:          
Generated 2024-03-28 21:48:11 | 0.032s | Q:39