News : The level of daily SPAM has reached insane proportions, all registrations are now manual.
I ask you to send me an e-mail (john (at) murga (dot) org), to confirm that you want me to create an account for you.

I wonder if anyone knows if it's possible to slice up a single image in FLTK, specifically apply one portion of an image to one widget and another portion to a different image?

I was looking at draw(), which apparently allows for an offset, but I don't understand how it works. I either get a "Bad drawable" (something like that) error or segfault depending on what I try.

I'm building one of those old slide puzzles where you have to unscramble a picture, and was hoping I could use a single arbitrary image rather than having to create a series of pieces specifically for the puzzle.

Code:

ts=48 -- tile size
tc=6 -- number of tiles in one line
fr=10 -- frame size

function move_tile(t)
local my_x,my_y,movex,movey=t:x(),t:y()
-- tile must be adjacent to the missing piece
if (my_x == tile[hidden]:x() and math.abs(my_y-tile[hidden]:y()) == ts)
or (my_y == tile[hidden]:y() and math.abs(my_x-tile[hidden]:x()) == ts)
then
movex=tile[hidden]:x()
movey=tile[hidden]:y()
tile[hidden]:position(my_x,my_y)
t:position(movex,movey)
w:redraw()
end
end

w=fltk:Fl_Window(ts*tc+fr*2,ts*tc+fr*2,"slide puzzle")

-- array of tiles, top left to right, then move down
tile={}
-- allow space for the frame
row=fr; col=fr
for i=0,tc*tc-1 do
tile[i]=fltk:Fl_Button(col,row,ts,ts)
tile[i]:callback(move_tile)
tile[i]:label(i+1) -- simple numbers could be replaced by images
-- next piece is ts pixels to the right
col=col+ts
-- start the next row
if col == ts*tc+fr then col=fr;row=row+ts end
end

Well, it seems to be working very well. Thank you!
Here is a new improved version including the new getTiles()

NOTE: I tested the previous version once in Windows, and the desktop froze. I don't know if it was a problem with the script or with Windows, but just beware!

Another note of warning:
For some reason it segfaults when trying to load a transparent PNG. John's tile script succeeds with the very same image, so I don't know what I did to break it. Adding os.exit() just before getTiles() doesn't segfault, so i'm confused.

Code:

#!/usr/bin/murgaLua

-- slide puzzle for MurgaLua 0.6.4+
-- 2008 mikshaw

-- window and image size are determined by these variables
ts=48 -- tile size
tc=4 -- number of tiles in one line
fr=5 -- frame size

if not Fl_Image.getTiles then
fltk.fl_alert("This program requires murgaLua 0.6.4 or newer.\n\n"..
"http://www.murga-projects.com/murgaLua/index.html")
os.exit(1)
end

function load_callback(object)
-- mod of a function written by John Murga
fileName = fltk.fl_file_chooser("Choose an RGB image", "Image Files (*.{jpg,png})", nil, nil)
if fileName then
if image1 then image1:uncache() end -- don't know if this is needed
image1 = Fl_Shared_Image.get(fileName,ts*tc,ts*tc)
myImages = image1:getTiles(ts,ts)
for key,value in ipairs(myImages) do
tile[key-1]:image(value)
tile[key-1]:redraw()
end
scramble()
scrambutt:label("load an image")
end
end

function move_tile(t)
local my_x,my_y,movex,movey=t:x(),t:y()
-- tile must be adjacent to the missing piece
if (my_x == tile[hidden]:x() and math.abs(my_y-tile[hidden]:y()) == ts)
or (my_y == tile[hidden]:y() and math.abs(my_x-tile[hidden]:x()) == ts)
then
movex=tile[hidden]:x()
movey=tile[hidden]:y()
tile[hidden]:position(my_x,my_y)
t:position(movex,movey)
w:redraw()
end
if start==0 then -- don't check puzzle during scramble
local ok=0
for i=0,tc*tc-1 do
if tile[i]:x()==pos[i].col and tile[i]:y()==pos[i].row then
ok=ok+1
if ok==tc*tc then
reset_hidden()
fltk.fl_beep()
scrambutt:label(prize[math.random(1,table.getn(prize))])
for i=0,tc*tc-1 do tile[i]:set_output() end -- disable the buttons
break
end
end
end
end
end

function scramble()
-- this picks a random tile to attempt to move and
-- repeats the process many times. Simply placing
-- tiles in random locations could potentially make
-- the puzzle impossible to solve
for i=0,tc*tc-1 do tile[i]:clear_output() end -- enable the buttons
if hidden then reset_hidden() end
-- turn a random tile into the missing piece
hidden=math.random(0,tc*tc-1)
tile[hidden]:labeltype(fltk.FL_NO_LABEL)
tile[hidden]:box(fltk.FL_DOWN_BOX)
start=1
local scram=0
while scram < 10000 do
move_tile(tile[math.random(0,tc*tc-1)])
scram=scram+1
end
start=0
w:redraw()
end

-- array of tiles, top left to right, then move down
tile={}; pos={}
-- allow space for the frame
row=fr; col=fr
-- subtract one is used because the number of tiles starts at one
-- but the table starts at zero (easier to position them from zero)
for i=0,tc*tc-1 do
tile[i]=fltk:Fl_Button(col,row,ts,ts)
tile[i]:align(80)
tile[i]:callback(move_tile)
pos[i]={col=col,row=row}
-- next piece is ts pixels to the right
col=col+ts
-- start the next row
if col == ts*tc+fr then col=fr;row=row+ts end
end

I wonder if there might be a difference in the way variables are created. In Linux it seems that a global variable can be created within a function. Maybe on Mac a global variable has to be initialized outside a function before it can be set in a function? If so, I think the start variable may need to be initialized too (start=1).