Author Topic: Steve's Magical Paint Fill  (Read 8363 times)

0 Members and 1 Guest are viewing this topic.

This topic contains a post which is marked as Best Answer. Press here if you would like to see it.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Steve's Magical Paint Fill
« on: November 22, 2019, 11:49:50 pm »
Here's a paint fill with which I simply can not figure out the logic behind WHY it's doing what it's doing.  It seems quick.  It seems like it'd be useful.  It seems...  Impossibly Broken.

Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(640, 480, 32)
  2.  
  3.  
  4. LINE (100, 100)-(300, 300), -1, B
  5. FOR r = 99 TO 101 STEP .25
  6.     CIRCLE (300, 300), r, -1
  7. 'PAINT (301, 301), -1
  8.  
  9. fill 200, 200, &HFFFF0000, -1
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18. SUB fill (tx, ty, kolor AS _UNSIGNED LONG, bordercolor AS _UNSIGNED LONG)
  19.     x = tx: y = ty
  20.     DIM Array(_WIDTH, _HEIGHT) AS LONG
  21.     FOR x1 = 0 TO _WIDTH - 1
  22.         FOR y1 = 0 TO _HEIGHT - 1
  23.             IF POINT(x1, y1) = bordercolor THEN S(x1, y1) = -1 'This is actually Sneaky Steve Logic at its best!
  24.             'Here we're checking to see if our array matches the proper border color or not.
  25.             'If it doesn't, then it might be a spot to fill (which is 0 -- the same as our inital Array()).
  26.         NEXT
  27.     NEXT
  28.     MinX = x: MinY = y
  29.     MaxX = x: MaxY = y
  30.     Array(x, y) = 1
  31.     pass = 1
  32.     DO
  33.         finished = -1
  34.         np = pass + 1 'next pass
  35.         TMX = MinX: TMX2 = MaxX
  36.         TMY = MinY: TMY2 = MaxY
  37.         FOR y = TMY TO TMY2
  38.             FOR x = TMX2 TO TMX STEP -1
  39.                 fill_left:
  40.                 IF Array(x - 1, y) = S(x - 1, y) THEN
  41.                     Array(x - 1, y) = np
  42.                     finished = 0
  43.                     x = x - 1
  44.                     IF x < MinX THEN MinX = x
  45.                     GOTO fill_left
  46.                 END IF
  47.             NEXT
  48.             FOR x = TMX TO TMX2
  49.                 fill_right:
  50.                 IF Array(x + 1, y) = S(x + 1, y) THEN
  51.                     Array(x + 1, y) = np
  52.                     finished = 0
  53.                     x = x + 1
  54.                     IF x > MaxX THEN MaxX = x
  55.                     GOTO fill_right
  56.                 END IF
  57.             NEXT
  58.         NEXT
  59.         FOR x = TMX TO TMX2
  60.             FOR y = TMY2 TO TMY STEP -1
  61.                 fill_up:
  62.                 IF Array(x, y - 1) = S(x, y - 1) THEN
  63.                     Array(x, y - 1) = np
  64.                     finished = 0
  65.                     y = y - 1
  66.                     IF y < MinY THEN MinY = y
  67.                     GOTO fill_up
  68.                 END IF
  69.             NEXT
  70.             FOR y = TMX TO TMX2
  71.                 fill_down:
  72.                 IF Array(x, y + 1) = S(x, y + 1) THEN
  73.                     Array(x, y + 1) = np
  74.                     finished = 0
  75.                     y = y + 1
  76.                     IF y > MaxY THEN MaxY = y
  77.                     GOTO fill_down
  78.                 END IF
  79.             NEXT
  80.         NEXT
  81.  
  82.         pass = pass + 1
  83.     LOOP UNTIL finished
  84.  
  85.  
  86.     FOR x = MinX TO MaxY
  87.         FOR y = MinY TO MaxY
  88.             IF Array(x, y) THEN PSET (x, y), kolor
  89.         NEXT
  90.     NEXT

Now how am I painting inside the confines of that circle?  I've gave it a heckuva border; there shouldn't be any sort of leaks in it; so just how the heck am I doing the magic that I'm doing here??

For a glitch, this is a pretty cool glitch.  LOL!
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline STxAxTIC

  • Library Staff
  • Forum Resident
  • Posts: 1091
  • he lives
    • View Profile
Re: Steve's Magical Paint Fill
« Reply #1 on: November 23, 2019, 12:30:59 am »
I think I see what's going on but I'd like to know the overall goal before fixing it. This is just a run-o-the mill fill routine right? Just like PAINT basically?
You're not done when it works, you're done when it's right.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Steve's Magical Paint Fill
« Reply #2 on: November 23, 2019, 01:07:03 am »
I think I see what's going on but I'd like to know the overall goal before fixing it. This is just a run-o-the mill fill routine right? Just like PAINT basically?

Aye, but it’s skipping the boundary somehow.  LOL
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Steve's Magical Paint Fill
« Reply #3 on: November 23, 2019, 01:24:17 am »
Try filling other spots, I get subscript out of range allot, no fills.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Steve's Magical Paint Fill
« Reply #4 on: November 23, 2019, 01:41:03 am »
Try filling other spots, I get subscript out of range allot, no fills.

Aye.  There’s no error checking in there for off-screen checks yet.  (X < 0 for example). I just thought for a glitch, this was a fun one all by itself.  I have no clue how it’s skipping that circle edge.  LOL
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Steve's Magical Paint Fill
« Reply #5 on: November 23, 2019, 01:49:27 am »
Hi Steve,

It does look like it would be fast. I used your example on Paint Image thread to improve the one I was working on from Pathfinder like dumping a check on all 8 neighbors that needed 3 or 4 layers of circle to stop flooding, down to 4 neighbor checks and one circle now stops fill. I had heck of time with unknown floods testing bottom right corner. Turns out screen boundary is _width -1 and _height -1.

Code: QB64: [Select]
  1. _TITLE "PathFinder Paint mod"
  2. 'start code from PathFinder Paint mod
  3. DEFINT A-Z
  4. CONST xmax = 800, ymax = 600
  5. SCREEN _NEWIMAGE(xmax, ymax, 32)
  6. _SCREENMOVE 300, 40
  7. DIM bc AS _UNSIGNED LONG, start
  8.  
  9. start = TIMER
  10. bc = &HFFFF0000
  11. CIRCLE (500, 400), 100, bc
  12. Fill 510, 400, &HFFFFFF00, bc
  13.  
  14. CIRCLE (xmax - 1, ymax - 1), 400, bc
  15. Fill xmax - 10, ymax - 10, &HFFFF0088, bc
  16.  
  17. LINE (100, 100)-(500, 500), bc, B
  18. Fill 150, 150, &HFFFF0000, bc
  19. PRINT TIMER - start
  20.  
  21. SUB Fill (x0, y0, fillC AS _UNSIGNED LONG, bC AS _UNSIGNED LONG)
  22.     DIM W, H, parentF, tick, ystart, ystop, xstart, xstop, y, x
  23.     W = _WIDTH - 1: H = _HEIGHT - 1
  24.     DIM temp(W, H)
  25.     temp(x0, y0) = 1: parentF = 1
  26.     WHILE parentF = 1
  27.         parentF = 0: tick = tick + 1
  28.         ystart = max(y0 - tick, 0): ystop = min(y0 + tick, H)
  29.         y = ystart
  30.         WHILE y <= ystop
  31.             xstart = max(x0 - tick, 0): xstop = min(x0 + tick, W)
  32.             x = xstart
  33.             WHILE x <= xstop
  34.                 IF POINT(x, y) <> bC AND temp(x, y) = 0 THEN
  35.                     IF temp(max(0, x - 1), y) THEN
  36.                         temp(x, y) = 1: parentF = 1: PSET (x, y), fillC
  37.                     ELSEIF temp(min(x + 1, W), y) THEN
  38.                         temp(x, y) = 1: parentF = 1: PSET (x, y), fillC
  39.                     ELSEIF temp(x, max(y - 1, 0)) THEN
  40.                         temp(x, y) = 1: parentF = 1: PSET (x, y), fillC
  41.                     ELSEIF temp(x, min(y + 1, H)) THEN
  42.                         temp(x, y) = 1: parentF = 1: PSET (x, y), fillC
  43.                     END IF
  44.                 END IF
  45.                 x = x + 1
  46.             WEND
  47.             y = y + 1
  48.         WEND
  49.     WEND
  50.  
  51. FUNCTION rand% (lo%, hi%)
  52.     rand% = INT(RND * (hi% - lo% + 1)) + lo%
  53. FUNCTION min (n1, n2)
  54.     IF n1 > n2 THEN min = n2 ELSE min = n1
  55. FUNCTION max (n1, n2)
  56.     IF n1 < n2 THEN max = n2 ELSE max = n1
  57.  
  58.  

EDIT: 3 secs off time with DEFINT A-Z
« Last Edit: November 23, 2019, 01:59:47 am by bplus »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Steve's Magical Paint Fill
« Reply #6 on: November 23, 2019, 01:52:09 am »
Code: [Select]
SCREEN _NEWIMAGE(640, 480, 32)


LINE (100, 100)-(300, 300), -1, B
FOR r = 99 TO 101 STEP .25
    CIRCLE (300, 300), r, -1
NEXT
'PAINT (301, 301), -1

SLEEP
fill 200, 200, &HFFFF0000, -1








SUB fill (tx, ty, kolor AS _UNSIGNED LONG, bordercolor AS _UNSIGNED LONG)
    x = tx: y = ty
    DIM Array(_WIDTH, _HEIGHT) AS LONG
    DIM S(_WIDTH, _HEIGHT) AS LONG
    FOR x1 = 0 TO _WIDTH - 1
        FOR y1 = 0 TO _HEIGHT - 1
            IF POINT(x1, y1) = bordercolor THEN S(x1, y1) = -1 'This is actually Sneaky Steve Logic at its best!
            'Here we're checking to see if our array matches the proper border color or not.
            'If it doesn't, then it might be a spot to fill (which is 0 -- the same as our inital Array()).
        NEXT
    NEXT
    MinX = x: MinY = y
    MaxX = x: MaxY = y
    Array(x, y) = 1
    pass = 1
    DO
        finished = -1
        np = pass + 1 'next pass
        TMX = MinX: TMX2 = MaxX
        TMY = MinY: TMY2 = MaxY
        FOR y = TMY TO TMY2
            FOR x = TMX2 TO TMX STEP -1
                IF Array(x, y) = pass THEN
                    fill_left:
                    IF Array(x - 1, y) = S(x - 1, y) THEN
                        Array(x - 1, y) = np
                        finished = 0
                        x = x - 1
                        IF x < MinX THEN MinX = x
                        GOTO fill_left
                    END IF
                END IF
            NEXT
            FOR x = TMX TO TMX2
                IF Array(x, y) = pass THEN
                    fill_right:
                    IF Array(x + 1, y) = S(x + 1, y) THEN
                        Array(x + 1, y) = np
                        finished = 0
                        x = x + 1
                        IF x > MaxX THEN MaxX = x
                        GOTO fill_right
                    END IF
                END IF
            NEXT
        NEXT
        FOR x = TMX TO TMX2
            FOR y = TMY2 TO TMY STEP -1
                IF Array(x, y) = pass THEN
                    fill_up:
                    IF Array(x, y - 1) = S(x, y - 1) THEN
                        Array(x, y - 1) = np
                        finished = 0
                        y = y - 1
                        IF y < MinY THEN MinY = y
                        GOTO fill_up
                    END IF
                END IF
            NEXT
            FOR y = TMY TO TMY2
                IF Array(x, y) = pass THEN
                    fill_down:
                    IF Array(x, y + 1) = S(x, y + 1) THEN
                        Array(x, y + 1) = np
                        finished = 0
                        y = y + 1
                        IF y > MaxY THEN MaxY = y
                        GOTO fill_down
                    END IF
                END IF
            NEXT
        NEXT

        pass = pass + 1
    LOOP UNTIL finished


    FOR x = MinX TO MaxX
        FOR y = MinY TO MaxY
            IF Array(x, y) THEN PSET (x, y), kolor
        NEXT
    NEXT
END SUB

Almost sorted.  I think it just needs error checking now.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Steve's Magical Paint Fill
« Reply #7 on: November 23, 2019, 02:02:13 am »
What was causing the magic jump?

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Steve's Magical Paint Fill
« Reply #8 on: November 23, 2019, 07:01:47 am »
The final, working version, (I think):

Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(640, 480, 32)
  2.  
  3.  
  4. LINE (100, 100)-(300, 300), -1, B
  5. FOR r = 99 TO 101 STEP .25
  6.     CIRCLE (300, 300), r, -1
  7. 'PAINT (301, 301), -1
  8.  
  9. fill 20, 200, &HFFFF0000, -1
  10.  
  11.  
  12.  
  13.  
  14.  
  15.  
  16.  
  17.  
  18. SUB fill (tx, ty, kolor AS _UNSIGNED LONG, bordercolor AS _UNSIGNED LONG)
  19.     x = tx: y = ty
  20.     DIM Array(_WIDTH, _HEIGHT) AS LONG
  21.     FOR x1 = 0 TO _WIDTH - 1
  22.         FOR y1 = 0 TO _HEIGHT - 1
  23.             IF POINT(x1, y1) = bordercolor THEN S(x1, y1) = -1 'This is actually Sneaky Steve Logic at its best!
  24.             'Here we're checking to see if our array matches the proper border color or not.
  25.             'If it doesn't, then it might be a spot to fill (which is 0 -- the same as our inital Array()).
  26.         NEXT
  27.     NEXT
  28.     MinX = x: MinY = y
  29.     MaxX = x: MaxY = y
  30.     Array(x, y) = 1
  31.     pass = 1
  32.     DO
  33.         finished = -1
  34.         np = pass + 1 'next pass
  35.         TMX = MinX: TMX2 = MaxX
  36.         TMY = MinY: TMY2 = MaxY
  37.         FOR y = TMY TO TMY2
  38.             FOR x = TMX2 TO TMX STEP -1
  39.                 IF Array(x, y) = pass THEN
  40.                     fill_left:
  41.                     IF x > 0 THEN
  42.                         IF Array(x - 1, y) = S(x - 1, y) THEN
  43.                             Array(x - 1, y) = np
  44.                             finished = 0
  45.                             x = x - 1
  46.                             IF x < MinX THEN MinX = x
  47.                             GOTO fill_left
  48.                         END IF
  49.                     END IF
  50.                 END IF
  51.             NEXT
  52.             FOR x = TMX TO TMX2
  53.                 IF Array(x, y) = pass THEN
  54.                     fill_right:
  55.                     IF x < _WIDTH - 2 THEN
  56.                         IF Array(x + 1, y) = S(x + 1, y) THEN
  57.                             Array(x + 1, y) = np
  58.                             finished = 0
  59.                             x = x + 1
  60.                             IF x > MaxX THEN MaxX = x
  61.                             GOTO fill_right
  62.                         END IF
  63.                     END IF
  64.                 END IF
  65.             NEXT
  66.         NEXT
  67.         FOR x = TMX TO TMX2
  68.             FOR y = TMY2 TO TMY STEP -1
  69.                 IF Array(x, y) = pass THEN
  70.                     fill_up:
  71.                     IF y > 0 THEN
  72.                         IF Array(x, y - 1) = S(x, y - 1) THEN
  73.                             Array(x, y - 1) = np
  74.                             finished = 0
  75.                             y = y - 1
  76.                             IF y < MinY THEN MinY = y
  77.                             GOTO fill_up
  78.                         END IF
  79.                     END IF
  80.                 END IF
  81.             NEXT
  82.             FOR y = TMY TO TMY2
  83.                 IF Array(x, y) = pass THEN
  84.                     fill_down:
  85.                     IF y < _HEIGHT - 2 THEN
  86.                         IF Array(x, y + 1) = S(x, y + 1) THEN
  87.                             Array(x, y + 1) = np
  88.                             finished = 0
  89.                             y = y + 1
  90.                             IF y > MaxY THEN MaxY = y
  91.                             GOTO fill_down
  92.                         END IF
  93.                     END IF
  94.                 END IF
  95.             NEXT
  96.         NEXT
  97.  
  98.         pass = pass + 1
  99.     LOOP UNTIL finished
  100.  
  101.  
  102.     FOR x = MinX TO MaxX
  103.         FOR y = MinY TO MaxY
  104.             IF Array(x, y) THEN PSET (x, y), kolor
  105.         NEXT
  106.     NEXT
  107.  
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Marked as best answer by SMcNeill on November 23, 2019, 03:42:21 am

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Steve's Magical Paint Fill
« Reply #9 on: November 23, 2019, 07:56:19 am »
Cleaned up a bit, all variables defined so OPTION _EXPLICIT won't complain, here's a timed comparison of the routine vs PAINT:

Code: QB64: [Select]
  1.  
  2. SCREEN _NEWIMAGE(640, 480, 32)
  3.  
  4. t# = TIMER(0.01)
  5. FOR i = 1 TO 10
  6.     CLS
  7.     CIRCLE (320, 240), 200, -1
  8.     PAINT (320, 240), &HFFFF0000, -1
  9. t2# = TIMER(0.01)
  10. FOR i = 1 TO 10
  11.     CLS
  12.     CIRCLE (320, 240), 200, -1
  13.     fill 320, 240, &HFFFF0000, -1
  14. t3# = TIMER(0.01)
  15.  
  16. PRINT USING "##.### seconds with PAINT"; t2# - t#
  17. PRINT USING "##.### seconds with FILL"; t3# - t2#
  18.  
  19.  
  20. SUB fill (tx AS INTEGER, ty AS INTEGER, kolor AS _UNSIGNED LONG, bordercolor AS _UNSIGNED LONG)
  21.     DIM x AS INTEGER, y AS INTEGER
  22.     DIM W AS INTEGER, H AS INTEGER
  23.     DIM pass AS INTEGER, finished AS INTEGER, np AS INTEGER
  24.     DIM TMX AS INTEGER, TMX2 AS INTEGER
  25.     DIM TMY AS INTEGER, TMY2 AS INTEGER
  26.     DIM left AS INTEGER, x1 AS INTEGER, y1 AS INTEGER
  27.     DIM MinX AS INTEGER, MinY AS INTEGER
  28.     DIM MaxX AS INTEGER, MaxY AS INTEGER
  29.  
  30.     x = tx: y = ty
  31.  
  32.     W = _WIDTH: H = _HEIGHT
  33.     DIM Array(W, H) 'AS INTEGER
  34.     DIM S(W, H) 'AS INTEGER
  35.     STATIC M AS _MEM: M = _MEMIMAGE(_DEST)
  36.     FOR x1 = 0 TO W - 1
  37.         FOR y1 = 0 TO H - 1
  38.             IF _MEMGET(M, M.OFFSET + (y1 * W + x1) * 4, _UNSIGNED LONG) = bordercolor THEN S(x1, y1) = -1 'MRM replacement for point below
  39.             'IF POINT(x1, y1) = bordercolor THEN S(x1, y1) = -1 'This is actually Sneaky Steve Logic at its best!
  40.             'Here we're checking to see if our array matches the proper border color or not.
  41.             'If it doesn't, then it might be a spot to fill (which is 0 -- the same as our inital Array()).
  42.         NEXT
  43.     NEXT
  44.     MinX = x: MinY = y
  45.     MaxX = x: MaxY = y
  46.     Array(x, y) = 1
  47.     pass = 1
  48.     DO
  49.         finished = -1
  50.         np = pass + 1 'next pass
  51.         TMX = MinX: TMX2 = MaxX
  52.         TMY = MinY: TMY2 = MaxY
  53.         FOR y = TMY TO TMY2
  54.             FOR x = TMX2 TO TMX STEP -1
  55.                 IF Array(x, y) = pass THEN
  56.                     fill_left:
  57.                     IF x > 0 THEN
  58.                         IF Array(x - 1, y) = S(x - 1, y) THEN
  59.                             Array(x - 1, y) = np
  60.                             finished = 0
  61.                             x = x - 1
  62.                             IF x < MinX THEN MinX = x
  63.                             GOTO fill_left
  64.                         END IF
  65.                     END IF
  66.                 END IF
  67.             NEXT
  68.             FOR x = TMX TO TMX2
  69.                 IF Array(x, y) = pass THEN
  70.                     fill_right:
  71.                     IF x < W - 2 THEN
  72.                         IF Array(x + 1, y) = S(x + 1, y) THEN
  73.                             Array(x + 1, y) = np
  74.                             finished = 0
  75.                             x = x + 1
  76.                             IF x > MaxX THEN MaxX = x
  77.                             GOTO fill_right
  78.                         END IF
  79.                     END IF
  80.                 END IF
  81.             NEXT
  82.         NEXT
  83.         FOR x = TMX TO TMX2
  84.             FOR y = TMY2 TO TMY STEP -1
  85.                 IF Array(x, y) = pass THEN
  86.                     fill_up:
  87.                     IF y > 0 THEN
  88.                         IF Array(x, y - 1) = S(x, y - 1) THEN
  89.                             Array(x, y - 1) = np
  90.                             finished = 0
  91.                             y = y - 1
  92.                             IF y < MinY THEN MinY = y
  93.                             GOTO fill_up
  94.                         END IF
  95.                     END IF
  96.                 END IF
  97.             NEXT
  98.             FOR y = TMY TO TMY2
  99.                 IF Array(x, y) = pass THEN
  100.                     fill_down:
  101.                     IF y < H - 2 THEN
  102.                         IF Array(x, y + 1) = S(x, y + 1) THEN
  103.                             Array(x, y + 1) = np
  104.                             finished = 0
  105.                             y = y + 1
  106.                             IF y > MaxY THEN MaxY = y
  107.                             GOTO fill_down
  108.                         END IF
  109.                     END IF
  110.                 END IF
  111.             NEXT
  112.         NEXT
  113.         pass = pass + 1
  114.     LOOP UNTIL finished
  115.  
  116.     FOR y = MinY TO MaxY
  117.         FOR x = MinX TO MaxX
  118.             IF Array(x, y) THEN 'PSET (x, y), kolor
  119.                 IF left = 0 THEN left = x
  120.             ELSE
  121.                 IF left THEN LINE (left, y)-(x - 1, y), kolor, BF: left = 0
  122.             END IF
  123.         NEXT
  124.         IF left THEN LINE (left, y)-(x - 1, y), kolor, BF: left = 0
  125.     NEXT

A lot of work to create something which is 4-5 times slower than what we already have, but I'm rather happy with it. 

A few decent points in this one's corner:

This isn't recursive, so it shouldn't ever generate those memory/stack crashes that other experiments with recursive paint fills have.

This could be easily customized to work with Petr's or Bplus's image fill routines, or adapted for use with textures and such.

If someone really needed it to be faster still, they could go ahead and swap everything over to _MEM calls instead.  I think in the end, it'd be comparable in speed to our current PAINT speed, (or maybe even faster as _MEM is often 10x faster than non-mem graphics commands,) but I'm too lazy at the moment to do that.  As it currently exists is good enough for me.  ;)
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Steve's Magical Paint Fill
« Reply #10 on: November 23, 2019, 08:22:17 am »
What was causing the magic jump?

I wasn't checking to make certain that we were only expanding upon points found in our previous pass.  ( IF Array(x, y) = pass THEN )

I'm pretty happy with the lessons I taught myself with this one, and here's one you might like to take advantage of with your pathfinding algorithms Bplus:

Let's take a normal 10x10 grid and work some pathfinding on it as we have on the past.  Imagine the starting point is in the center, and we want to know how far it is to reach any other point.

PASS 1:
0000000000
0000000000
0000000000
0000000000
0000100000
0000000000
0000000000
0000000000
0000000000
0000000000

PASS 2:
0000000000
0000000000
0000000000
0000200000
0002120000
0000200000
0000000000
0000000000
0000000000
0000000000

PASS 3:
0000000000
0000000000
0000300000
0003230000
0032123000
0003230000
0000300000
0000000000
0000000000
0000000000

With each process, we've been checking the whole array and incrementing the surrounding points if they match our current pass...

My EUREKA moment, which really changes the performance levels of this type of work, came with the complexity of using PAINT with pathfinding on the whole screen.  That's a TON of pixels to check each pass...

Instead, what I'm doing here is this leap of logic:

PASS 1
1

PASS 2
020
212
020

PASS 3
00300
03230
32123
03230
00300

Here, I set MinX to 1 less, MaxX to 1 more, MinY to 1 less, MaxY to 1 more each pass.  I don't check the whole array each time around; I only check to the points which are relevant and possible on this given pass.

On Pass 3, why do I need to check point (0,0), if my hero is starting at point(10,10)??

Once I came to this little revelation in logic, I could ignore checking a large portion of my screen and times improved dramatically, and I see no reason why this same logic wouldn't apply to the way we've been doing pathfinding with 2D arrays either.

Only check the area you need to check; no more, no less.

******************

I think we can also eliminate checking a whole swath of points each pass, if we ONLY bother to check the outer rows like this, but I haven't implemented it yet:

For example, take a look at PASS 3:
PASS 3
00300
03230
32123
03230
00300

I don't see any reason why we couldn't exclude the following on our next pass:
00300
0XXX0
3XXX3
0XXX0
00300

There should be some simple formula we could plug in to say IF X > this point AND X < that point and Y > this point and Y < that point THEN we don't need to check these values... Once that's sorted out and plugged in, the larger the pathfinding gets, the more of the results we can exclude from checking over and over and over again...

I imagine it'd make as large of a leap in performance as the first EUREKA moment of only checking MinX to MaxX and MinY to MaxY did.  I just haven't sorted out the math needed for the exclusion yet, but my mind is visualizing the concept fairly nicely at least.  ;)

EDIT:

(This might be as simple as a (PASS \ 2 -1 ) type exception.  I need more coffee and a lot of testing with the concept before I'd swear to it, but I'm thinking it'll fit our pattern for segments we can safely exclude.) 
« Last Edit: November 23, 2019, 08:30:24 am by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Steve's Magical Paint Fill
« Reply #11 on: November 23, 2019, 10:42:03 am »
Dang Steve, that's lightning fast compared to my Fill routine tests, like 100 x's.

I've been expanding out from PAINT point all along since Pathfinder, something else is making this faster.

$CHECKING ON/OFF is not it, not much difference with or without.

I will have to study more, nice one Steve!

I hope your wife is doing better,
Mark

PS here is my test code, same as I was running times on with my Fill routine, mine was down to 8 secs, yours .1... wow! And does work fine with OPTION _EXPLICIT.
Code: QB64: [Select]
  1. OPTION _EXPLICIT ''Fill test by Steve routine  2019-11-22 mod to compare tp test times of mine
  2. _TITLE "PathFinder Paint mod" 'b+ 2019-11-23
  3. 'start code from PathFinder Paint mod
  4. DEFINT A-Z
  5. CONST xmax = 800, ymax = 600
  6. SCREEN _NEWIMAGE(xmax, ymax, 32)
  7. _SCREENMOVE 300, 40
  8. DIM bc AS _UNSIGNED LONG, start#
  9.  
  10. start# = TIMER
  11. bc = &HFFFF0000
  12. CIRCLE (500, 400), 100, bc
  13. fill 510, 400, &HFFFFFF00, bc
  14.  
  15. CIRCLE (xmax - 1, ymax - 1), 400, bc
  16. fill xmax - 10, ymax - 10, &HFFFF0088, bc
  17.  
  18. LINE (100, 100)-(500, 500), bc, B
  19. fill 150, 150, &HFFFF0000, bc
  20. PRINT TIMER - start#
  21.  

Yours also handles that bottom right corner.

Oh!  I don't think skipping inner rechecks will work for more complicated Fills, you need to go back in and check nooks and crannies missed or left unfinished in previous passes. I will work up a more rigorous test (but I've yet to learn why this is so fast).

« Last Edit: November 23, 2019, 10:56:47 am by bplus »

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Steve's Magical Paint Fill
« Reply #12 on: November 23, 2019, 10:59:07 am »
There's a couple of more tricks in here which improve performance.

Trick 2 is expanding to the limits each pass.  If I go back to a 10x10 grid to illustrate, let's let B represent the border and fill from some point in the middle:


PASS 1:
BBBBBBBBBB
B00000000B
B00000000B
B00000000B
B00000000B
B00010000B
B00000000B
B00000000B
B00000000B
BBBBBBBBBB

PASS 2:
BBBBBBBBBB
B00020000B
B00020000B
B00020000B
B00020000B
B22212222B
B00020000B
B00020000B
B00020000B
BBBBBBBBBB

PASS 3:
BBBBBBBBBB
B33323333B
B33323333B
B33323333B
B33323333B
B22212222B
B33323333B
B33323333B
B33323333B
BBBBBBBBBB


Three passes, and we're finished.  MinX, MaxX, MinY and MaxY expand as we push our boundaries, and we only make a few runs through the whole DO... LOOP until we're finished.
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Steve's Magical Paint Fill
« Reply #13 on: November 23, 2019, 11:07:44 am »
Oh so 2 expands out x, y directions from 1, and 3 expands out each of level 2 cells, 3 passes that's it! that's the new one to me, thanks.

I was thinking a maze fill would be perfect test for nook and cranny catching.

Offline SMcNeill

  • QB64 Developer
  • Forum Resident
  • Posts: 3972
    • View Profile
    • Steve’s QB64 Archive Forum
Re: Steve's Magical Paint Fill
« Reply #14 on: November 23, 2019, 11:19:06 am »
Trick #3 is this one, but I think it's the hardest to explain.  Let me know if this explaination doesn't make any sense to you.  The super secret code is here:

Code: [Select]
    FOR x1 = 0 TO W - 1
        FOR y1 = 0 TO H - 1
            IF _MEMGET(M, M.OFFSET + (y1 * W + x1) * 4, _UNSIGNED LONG) = bordercolor THEN S(x1, y1) = -1 'MRM replacement for point below
            'IF POINT(x1, y1) = bordercolor THEN S(x1, y1) = -1 'This is actually Sneaky Steve Logic at its best!
            'Here we're checking to see if our array matches the proper border color or not.
            'If it doesn't, then it might be a spot to fill (which is 0 -- the same as our inital Array()).
        NEXT
    NEXT

We're not actually plotting to the screen as we go along; instead we're storing our changes in an array and then implementing them all at once when we're finished.  This avoids us having to PSET point by point, and then get information back from each POINT to see if colors match.

Array() is the array which contains the information about what I want to change, or not.
S() is the array to store the screen information once, so I don't have to use POINT over and over.

And the little code above prepares the routine for a little tricky logic -- it eliminates the need for us to check colors at all!!

If this case, S() compares pixel by pixel ONCE to see which points on our screen are the border colors.  Those are *the only* colors we care about.  It doesn't matter if a point is Red, Blue, or Green, if we're painting until we reach White, we cover all those up...  We can turn this array into a simple binary array of "YES, this point is the border color", or "NO, this point isn't the border color."

Now, since Array() is going to be our array to hold our changes, it starts out as being all values of ZERO.

We set our PASS to 1, and make the center point we want to fill = PASS.

At this point, the program then starts the DO... LOOP process:

DO
    check each point to if they're equal to PASS.
        IF they are, then check to see if the points beside them are EQUAL TO Array() at the same point... 
            IF they are, make them EQUAL PASS and continue to check the points along the same axis... 
LOOP

As we change our Array(), keep in mind that unaltered points have a value of 0...
And we set S() so that NON-border colors have a value of 0...

Only IF Array(x,y) = S(x,y) is it a point to fill in!!

One comparison which we have to do over and over, simplified down as much as I could figure out how to simplify it....


Compare it to what I was checking for over in the version I posted in your paint fill topic:

 IF Array(x, y - 1) = 0 AND POINT(x, y - 1) <> backkolor THEN Array(x, y - 1) = pass + 1: finished = 0

All that comparison in red was taken care of once, in the precalculations for me.  All I need to know is now:

IF Array(x, y - 1) = S(x, y -1) THEN...



If that makes any sense at all??
« Last Edit: November 23, 2019, 11:23:27 am by SMcNeill »
https://github.com/SteveMcNeill/Steve64 — A github collection of all things Steve!