Automatic movement of pixels via scripting (help)

I’m looking for a way to have the program deselect a column, move the rest of the selection down one pixel, and repeat until all the pixels have been shifted.

And then a way to do the same, but the opposite side (deselect the right most column and move the rest down one pixel, repeat).

The finished product would be like this:

Is this something that is even possible, or something someone else has already done?

This could be done via rather simple script. But for best results it should be script with dialog to select mode of slicing and parameters of offsetting selected area. Hmm, interesting idea.

Admittedly, I don’t know enough about LUA to do this. I’m attempting to read through the API, but it isn’t my strong suit.

Could you point me in the general direction of where I should start if I have to figure this out myself?

I’m really just trying to speed up my workflow when it comes to making tilesets. Its tedious to do it manually.

Search thru API for functions and objects methods that will help to get current selection and its bounding rect, access current frame and layer cel object, clone cel’s image data, then write data from cloned image back to new positions of cel’s image with clearing pixels that should be cleared. Then free cloned image.

I have so far figured out how to select a tile with a button press, and move it down one pixel. The rest of what you said is beyond me for the time being, but I’m going to keep at it.

I got a really shoddy version of it, but I made it work!

local sprite = app.activeSprite
app.command.SelectTile()
for i = 16,1,-1
	do
		app.command.MoveMask{ target="boundaries", direction="right", units="pixel", quantity=1}
		app.command.MoveMask{ target="content", direction="down", units="pixel", quantity=1}
	end



2 Likes

Excellent! This is a perfect way to do it too :slight_smile: I’d call it automation.

As compared to altering pixels data. Automation is faster to code and it may often work faster too. Just because it uses existing optimized operations, only ordered as you want them. Which is perfectly OK.

On the other side altering data is a more general way to make any modifications and transformations to each and every pixel by its own rules. For example, you can relatively easiily generate swirl-and-vanish animation for any image, etc.

hi DaddiSenpai, i’ve added few lines, so now you’ll get dialog window where you can enter offset values. negative values skew the content of selection to left and/or up, positive to right and/or down:

-- skew selection 
-- MAKE SELECTION FIRST 

local sprt = app.activeSprite
-- default values if user doesn't enter anything 
local o_x = 1 -- horizontal offset 
local o_y = 1 -- vertical offset 

local dlgWin = Dialog{ title = "*** SKEWER 0.1 ***  " } 

-- set dialog window 
dlgWin
	:number{ id = "x_offset",
            label = "offset x"
	}
	
	:number{ id = "y_offset",
            label = "offset y"
	}
	
	:button{ 
		text = "OK", 
		onclick = function()
			fMain(dlgWin) 
		end 
	} 
	
-- show dialog window 
dlgWin:show{ wait = false } 

function fSkew(r_w, o_x, o_y) 
	-- don't allow values to be zero and break things 
	if o_x == 0 then 
		o_x = 1 
	end 
	if o_y == 0 then 
		o_y = 1 
	end 
	
	-- skew 
	for i = r_w / math.abs(o_x), 1, -1 
	do  
		-- direction right or left? 
		if o_x > 0 then 
			app.command.MoveMask{ target="boundaries", direction="right", units="pixel", quantity = o_x}
		elseif o_x < 0 then 
			app.command.MoveMask{ target="boundaries", direction="left", units="pixel", quantity = math.abs(o_x)}
		end 
		
		-- direction down or up?  
		if o_y > 0 then 
			app.command.MoveMask{ target="content", direction="down", units="pixel", quantity = o_y}
		elseif o_y < 0 then 
			app.command.MoveMask{ target="content", direction="up", units="pixel", quantity = math.abs(o_y)}
		end 
	end
	
end 

function fMain(dlgWin) 
	
	local status = sprt.selection.isEmpty
	local rectangle = sprt.selection.bounds 
	local r_w = rectangle.width
	local r_h = rectangle.height 
	
	-- check if selection isn't empty 
	if status == false then 
		o_x = dlgWin.data.x_offset 
		o_y = dlgWin.data.y_offset 
		fSkew(r_w, o_x, o_y) 
	else 
		app.alert("Make selection!") 
	end 
	
end 

i’ve found few issues so far:

sprt.selection:deselect() command sometimes doesn’t work - it seems like the script in some cases won’t finish properly.
however it does what it should and ctrl+z works aswell.

also wrapping app.command.MoveMask in app.transaction( function() ... end) to get only one undo history entry crashes aseprite almost all the time. i guess this might be a bug?

hence neither selection:deselect nor app.transaction are in the script now and if you get an error:

A problem has occurred.

Details:
Cannot modify the sprite.
It is being used by another command.
Try again.

click on cancel and hit escape and it will be fine.

however, just to be safe - always save your work before using this script.

2 Likes

This is exactly what I had envisioned from the start. Thank you so much.

I did notice the undo bug with transaction(function()end) and reported it last night. Its been around for awhile apparently as I found it in the beta and the live version.

1 Like
-- skew selection 
-- MAKE SELECTION FIRST 

local sprt = app.activeSprite
-- default values if user doesn't enter anything 
local o_x = 1 -- horizontal offset 
local o_y = 1 -- vertical offset 

local dlgWin = Dialog{ title = "*** SKEWER 0.1 ***  " }

local function cancelWizard(dlgWin)
	dlgWin:close()
end 

-- set dialog window 
dlgWin
	:number{ id = "x_offset",
            label = "offset x"
	}
	
	:number{ id = "y_offset",
            label = "offset y"
	}
	
	:button{
		text="Cancel",
		onclick = function()
			cancelWizard(dlgWin)
		end
	}
	:button{ 
		text = "OK", 
		onclick = function()
			fMain(dlgWin) 
		end 
	} 
	
-- show dialog window 
dlgWin:show{ wait = false } 

function fSkew(r_w, o_x, o_y) 
	-- don't allow values to be zero and break things 
	if o_x == 0 then 
		o_x = 1 
	end 
	if o_y == 0 then 
		o_y = 1 
	end 
	
	-- skew 
	for i = r_w / math.abs(o_x), 1, -1 
	do  
		-- direction right or left? 
		if o_x > 0 then 
			app.command.MoveMask{ target="boundaries", direction="right", units="pixel", quantity = o_x}
		elseif o_x < 0 then 
			app.command.MoveMask{ target="boundaries", direction="left", units="pixel", quantity = math.abs(o_x)}
		end 
		
		-- direction down or up?  
		if o_y > 0 then 
			app.command.MoveMask{ target="content", direction="down", units="pixel", quantity = o_y}
		elseif o_y < 0 then 
			app.command.MoveMask{ target="content", direction="up", units="pixel", quantity = math.abs(o_y)}
		end 
	end
	
end 

function fMain(dlgWin) 
	
	local status = sprt.selection.isEmpty
	local rectangle = sprt.selection.bounds 
	local r_w = rectangle.width
	local r_h = rectangle.height 
	
	-- check if selection isn't empty 
	if status == false then 
		o_x = dlgWin.data.x_offset 
		o_y = dlgWin.data.y_offset 
		fSkew(r_w, o_x, o_y) 
	else 
		app.alert("Make selection!") 
	end 
	
end 


I tweaked it and added a cancel button. I was having issues closing the prompt if I had something selected still, this fixes it.

2 Likes