I wanted to achieve this effect with fill patterns for a recent project I started so I figured I'd share it here incase anyone else is interested in using it.

The local fill pattern function is shifting each row/column in the fill pattern by given x and y amount. It's using binary arithmetics and a cache of shift masks.

Here's the original code for version 1.0, takes 126 tokens:

**EDIT:**

With great contributions and improvements by Felice here's a both faster, smaller and more API consistent **version 2.0**, takes 87 tokens:

Hope you find it useful!

Excellent work. I've been wondering about how to implement this idea since fillp was released.

Good idea!

I refined your methods and took a fair chunk of the math and tokens off (now 80 tokens). Here's my version:

_fillp_xmask_lo={[0]=0xffff,0x7777,0x3333,0x1111} _fillp_xmask_hi={[0]=0x0000,0x8888,0xcccc,0xeeee} function fillp_local(x,y,p) x=band(x,3) local p16=flr(p) local p32=rotr(p16+lshr(p16,16),band(y,3)*4+x) return fillp(p-p16+flr(band(p32,_fillp_xmask_lo[x])+band(rotl(p32,4),_fillp_xmask_hi[x]))) end |

And a demo thereof:

Thanks Felice,

this is a very nice addition. Would you mind if I edit the original post with an update of your refined version?

Edit: I just noticed however, that your version doesn't respect the additional (floating point?) bits you can provide to a pattern to specify ignoring the second color from drawing. For instance, this pattern would not draw as intended:

local pattern=0b1110100110011010.1 fillp_local(x,y,pattern) |

Once that is fixed I'll update the OP with your improvements, given you're alright with that.

Regards.

Do as you like with the code, I marked it creative commons for that reason. :)

It should be retaining the fractional transparency bit. I don't remember testing that so I may have failed to do it right. That's what the **p-p16** is where I send the value on to fillp--the original pattern minus the integer bits, leaving the fractional bits, which are added to the resulting shifted pattern.

Oh, okay, I see what I did.

When I was rearranging code, I accidentally moved that **p-p16** inside of the **flr()**, effectively deleting it.

Easy fix. See my original post for an updated version and a demo that <gasp> actually tests the transparency support. ;)

Actually...

One option would be to write a flat-out replacement for **fillp()** that takes optional x,y args *after* the pattern and shifts the pattern if so. That would add to the API pretty seamlessly, while not adding complexity and still keeping backwards compatibility.

Something like this:

_fillp_xmask_lo={[0]=0xffff,0x7777,0x3333,0x1111} _fillp_xmask_hi={[0]=0x0000,0x8888,0xcccc,0xeeee} _fillp_original=fillp function fillp(p,x,y) if y then x=band(x,3) local p16=flr(p) local p32=rotr(p16+lshr(p16,16),band(y,3)*4+x) p+=flr(band(p32,_fillp_xmask_lo[x])+band(rotl(p32,4),_fillp_xmask_hi[x]))-p16 end return _fillp_original(p) end |

It costs 7 more tokens, but it just feels more natural, and in keeping with the rest of the API, to extend an existing function. Other calls like **map()** and **sspr()** do similar things.

Obligatory demo:

Felice, this is a way better approach so I'll update the original post with your improvements. Thanks and great work!

Ah, nice. I like my demo for having my name in it, but yours definitely demonstrates the functionality better. ;)

67 tokens with more arithmetic, no tables, and an otherwise pointless local x (not counting tokens for backward compatibility defaults)

local _fillp = fillp local function fillp(p, x, y) p, x, y = p or 0, x or 0, y or 0 -- to maintain drop-in replacement compatibility with fillp(p) local p16, x = flr(p), band(x, 3) local f, p32 = flr(15 / 2 ^ x) * 0x1111, rotr(p16 + lshr(p16, 16), band(y, 3) * 4 + x) return _fillp(p - p16 + flr(band(p32, f) + band(rotl(p32, 4), 0xffff - f))) end |

and thanks to Felice's response on Discord, here it is with shl instead of ^, faster but two more tokens

local _fillp = fillp local function fillp(p, x, y) p, x, y = p or 0, x or 0, y or 0 -- to maintain drop-in replacement compatibility with fillp(p) local p16, x = flr(p), band(x, 3) local f, p32 = flr(15 / shl(1,x)) * 0x1111, rotr(p16 + lshr(p16, 16), band(y, 3) * 4 + x) return _fillp(p - p16 + flr(band(p32, f) + band(rotl(p32, 4), 0xffff - f))) end |

Felice, I am curious why your natural extension above shifts neither x nor y when y is not present? If we want a seamless extension shouldn't it still shift x?

I was just economizing. I figured anyone wanting to shift the pattern would probably provide both parameters and that a single parameter would generally be an accident.

It's entirely reasonable to want to work with just x though. I just didn't do it. Arbitrary.

I happened to come back and look at this. Isn't this:

flr(15 / shl(1,x)) * 0x1111 |

Just this?

flr(shr(15,x)) * 0x1111 |

I have been banging my head all morning trying to do this and then I found this! thanks a lot

@Felice is a god when it comes to math/low-level. Let me know if you guys want me to update OP with any new/better version.

bumping a very old thread here, since I am using this function in a project I am working on. Thanks so much to the collaboration here!

@sparr and @Felice - I have a fundamentals question. Why are you using declaring "fillp" and "_fillp" as "local" above, if they are getting dropped into the global namespace?

I don't know why fillp is declared as local in the versions above, but to avoid adding _fillp to the global namespace, you can use a do/end block, like so:

do local _fillp = fillp function fillp(p, x, y) p, x, y = p or 0, x or 0, y or 0 -- to maintain drop-in replacement compatibility with fillp(p) local p16, x = flr(p), band(x, 3) local f, p32 = flr(15 / shl(1,x)) * 0x1111, rotr(p16 + lshr(p16, 16), band(y, 3) * 4 + x) return _fillp(p - p16 + flr(band(p32, f) + band(rotl(p32, 4), 0xffff - f))) end end |

Not a programmer but I'm glad you shared this, I'm sure I'll make use if it if I ever were to dip my toes into making pico8 programs.

Here's a faster + token-optimized version, using bitwise operators instead of functions. 53 tokens:

-- usage: fillp_local(∧,-%0x5f28,-%0x5f2a) --world-based fillp instead of camera-based function fillp_local(p,x,y) x&=3 local p16=p&0xffff -- local mask,p32=(15>>x)\1*0x1111,p16+(p16>>>16) >>< y*4+x local mask,p32=split"0xffff,0x7777,0x3333,0x1111"[x+1],p16+(p16>>>16) >>< y*4+x return fillp(p32&mask | p32<<>4 & 0xffff-mask | p-p16) end --example program: x=0 y=0 function _draw() x+=tonum(btn(1))-tonum(btn(0)) y+=tonum(btn(3))-tonum(btn(2)) cls(1) camera(x-64,y-64) fillp_local(∧,-%0x5f28,-%0x5f2a) rectfill(-1000,-1000,1000,1000,12) --waves fillp() circfill(x,y,2,8) end |

(the `%0x5f28`

stuff gets the current camera position)

[Please log in to post a comment]