Sprite rotation and negative scale

Hello Playdate team!

I've been enjoying the Playdate SDK very much. It's a fun development playground, and the crank is intriguing. The features and documentation have helped me get some projects started. Although the 1-bit display sounds restrictive, it has actually sparked some creativity. They say that constraints boost creativity.

In particular, the SDK's image features afford a lot of flexibility when drawing things to the screen. One of my projects requires rotating and scaling sprites. When rotating a sprite that has a negative scale, the sprite disappears. Also, the scale has to be set after setting the rotation, otherwise the scale appears to be reset for sprites that have a negative y scale and a positive x scale.

I can work around this problem by creating a blank image; pushing that image onto the graphics context; drawing a scaled, unrotated version of the desired image; popping the context; and rotating the drawn image.

Example code and Playdate Simulator recordings follow.

I am using a 2020 Mac M1 mini.

I look forward to hearing back from you.

Thanks!

REPLICATION

local rotation = 0

function makeSprite(offsetX, offsetY, scaleX, scaleY)
    local sprite <const> = playdate.graphics.sprite.new(playdate.graphics.image.new("Sprites/robot"))
    sprite:setScale(scaleX, scaleY)
    sprite:add()
    sprite:moveTo((playdate.display.getWidth() / 2) + offsetX, (playdate.display.getHeight() / 2) + offsetY)
    return sprite
end

local sprites <const> = {
    makeSprite(-150, -50, 1, 1),  -- unscaled
    makeSprite(-50, -50, -1, 1),  -- negative x scale
    makeSprite(50, -50, 1, -1),   -- negative y scale
    makeSprite(150, -50, -1, -1), -- negative x and y scale
    makeSprite(-150, 50, 2, 2),   -- upscaled
    makeSprite(-50, 50, -2, 2),   -- upscaled, negative x scale
    makeSprite(50, 50, 2, -2),    -- upscaled, negative y scale
    makeSprite(150, 50, -2, -2)   -- upscaled, negative x and y scale
}

function playdate.update()
    rotation += 1
    for i = 1, #sprites do
        local sprite <const> = sprites[i]
        local scaleX <const>, scaleY <const> = sprite:getScale()
        sprite:setRotation(rotation)
        sprite:setCollideRect(0, 0, sprite:getSize())
        sprite:setScale(scaleX, scaleY)
    end
    playdate.graphics.sprite.update()
end

invisible_sprites

WORKAROUND

local rotation = 0

function makeSprite(offsetX, offsetY, scaleX, scaleY)
    local image <const> = playdate.graphics.image.new("Sprites/robot")
    local imageSizeX <const>, imageSizeY <const> = image:getSize()
    local spriteImage = playdate.graphics.image.new(math.abs(imageSizeX * scaleX), math.abs(imageSizeY * scaleY))
    local sprite <const> = playdate.graphics.sprite.new(spriteImage)
    playdate.graphics.pushContext(sprite:getImage())
    image:scaledImage(scaleX, scaleY):draw(0, 0)
    playdate.graphics.popContext()
    sprite:add()
    sprite:moveTo((playdate.display.getWidth() / 2) + offsetX, (playdate.display.getHeight() / 2) + offsetY)
    return sprite
end

local sprites <const> = {
    makeSprite(-150, -50, 1, 1),  -- unscaled
    makeSprite(-50, -50, -1, 1),  -- negative x scale
    makeSprite(50, -50, 1, -1),   -- negative y scale
    makeSprite(150, -50, -1, -1), -- negative x and y scale
    makeSprite(-150, 50, 2, 2),   -- upscaled
    makeSprite(-50, 50, -2, 2),   -- upscaled, negative x scale
    makeSprite(50, 50, 2, -2),    -- upscaled, negative y scale
    makeSprite(150, 50, -2, -2)   -- upscaled, negative x and y scale
}

function playdate.update()
    rotation += 1
    for i = 1, #sprites do
        local sprite <const> = sprites[i]
        sprite:setRotation(rotation)
        sprite:setCollideRect(0, 0, sprite:getSize())
    end
    playdate.graphics.sprite.update()
end

Here is an example of the expected behavior. I couldn't put it in the post, since I am too new here.
visible_sprites