LAST VERSION as USED in R-type 1.4 after optimizing it a bit further:
--97 tokens with Scaling and arbitrary size function pd_rotate(x,y,rot,mx,my,w,flip,scale) scale=scale or 1 w*=scale*4 local cs, ss = cos(rot)*.125/scale,sin(rot)*.125/scale local sx, sy = mx+cs*-w, my+ss*-w local hx = flip and -w or w local halfw = -w for py=y-w, y+w do tline(x-hx, py, x+hx, py, sx-ss*halfw, sy+cs*halfw, cs, ss) halfw+=1 end end |

This is a general-purpose sprite rotation function using tline with support for scaling and flipping
It always draw a screen-oriented rectangular area, (centered at x,y) large enough (w) to contain the rotated tiles (centered at mx,my), and calculates the rotated (rot 0 to 1) tline coordinates and deltas.
The catch is that you need to have the sprite in the map and must leave "clearance-space" around the map part to be rotated (matching the largest side of the sprite or you get some additional map tiles drawn as well, visible in pink in the example). But you can just prepare one rotating area and just swap tiles with mset().
LAST EDITED for a mistake that was messing up mx and my


see https://www.lexaloffle.com/bbs/?tid=37561 for a generic purpose tlne rotation without any constraints


Thanks again @TheRoboZ for posting this.
I've just started playing with it as a successor to Fred72's legendary RSPR() routine I'd been using for ages.
Early days, but looks like it might do the trick
(although, due to the way I render shadows, etc. I might end up using more tokens - might be worth it, depends on the perf improvement!)
Thanks again! 🤓👍
UPDATE: Woah, looks like mine + Fred's comments overlapped!
Thx @freds72, I'll give that another look, but I thought the token count was higher and/or inc. features I do not require (as all my current needs are for single, 8x8 sprites) 🤔


I noticed a small optimization after posting:
--150 tokens
function draw_rotated_rect(x,y,sw_rot,mx,my,w,h)
local dx, dy, r, cs, ss = 0, 0, max(w,h)/2, cos(sw_rot), -sin(sw_rot)
if w>h then dy = (w-h)/2 else dx = (h-w)/2 end
local ssx, ssy, cx, cy = mx - 0.3 -dx, my - 0.3 -dy, mx+r-dx, my+r-dy
ssy -=cy
ssx -=cx
local delta_px = max(-ssx,-ssy)*8
--this just draw a bounding box to show the exact draw area
rect(x-delta_px,y-delta_px,x+delta_px,y+delta_px,5)
local sx, sy = cs * ssx + cx, -ss * ssx + cy
for py = y-delta_px, y+delta_px do
tline(x-delta_px, py, x+delta_px, py, sx + ss * ssy, sy + cs * ssy, cs/8, -ss/8)
ssy+=1/8
end
end
--108 tokens
function draw_rotated_square(x,y,sw_rot,mx,my,r)
local cs, ss = cos(sw_rot), -sin(sw_rot)
local ssx, ssy, cx, cy = mx - 0.3, my - 0.3, mx+r/2, my+r/2
ssy -=cy
ssx -=cx
local delta_px = -ssx*8
--this just draw a bounding box to show the exact draw area
rect(x-delta_px,y-delta_px,x+delta_px,y+delta_px,5)
local sx, sy = cs * ssx + cx, -ss * ssx + cy
for py = y-delta_px, y+delta_px do
tline(x-delta_px, py, x+delta_px, py, sx + ss * ssy, sy + cs * ssy, cs/8, -ss/8)
ssy+=1/8
end
end
and for fixed 8x8 size you can optimize it even more.


Hey @Liquidream I added a more general purpose one with support for flip and scaling after using it in my game
function draw_rotated_tile(x,y,rot,mx,my,w,flip,scale) scale = scale or 1 w+=.8 local halfw, cx = scale*-w/2, mx + w/2 -.4 local cs, ss, cy = cos(rot)/scale, -sin(rot)/scale, my-halfw/scale-.4 local sx, sy, hx, hy = cx + cs*halfw, cy - ss*halfw, w*(flip and -4 or 4)*scale, w*4*scale --this just draw a bounding box to show the exact draw area rect(x-hx,y-hy,x+hx,y+hy,5) for py = y-hy, y+hy do tline(x-hx, py, x+hx, py, sx + ss*halfw, sy + cs*halfw, cs/8, -ss/8) halfw+=1/8 end end |


Since this has been used in a couple carts, here is the last updated version as used in R-type:
--123 tokens with scaling and arbitrary size
function pd_rotate(x,y,rot,mx,my,w,flip,scale)
scale=scale or 1
local halfw, cx=scale-w/2, mx + .5
local cy,cs,ss=my-halfw/scale,cos(rot)/scale,sin(rot)/scale
local sx, sy, hx, hy=cx+cshalfw, cy+sshalfw, w(flip and -4 or 4)scale, w4scale
for py=y-hy, y+hy do
tline(x-hx, py, x+hx, py, sx -sshalfw, sy + cs*halfw, cs/8, ss/8)
halfw+=.125
end
end


Since this has been used in a couple carts, here is the last updated version as used in R-type:
--123 tokens with scaling and arbitrary size function pd_rotate(x,y,rot,mx,my,w,flip,scale) scale=scale or 1 local halfw, cx=scale*-w/2, mx + .5 local cy,cs,ss=my-halfw/scale,cos(rot)/scale,sin(rot)/scale local sx, sy, hx, hy=cx+cs*halfw, cy+ss*halfw, w*(flip and -4 or 4)*scale, w*4*scale for py=y-hy, y+hy do tline(x-hx, py, x+hx, py, sx -ss*halfw, sy + cs*halfw, cs/8, ss/8) halfw+=.125 end end |


Here is @simonwilson's rocket. I wanted to see if this rotation was any better with less jaggies than the one he wrote. I'm not seeing a difference.


@dw817 For R-type I was constraining rotation angles to fixed step (when drawn, calculation uses continuous rotation)
function pd_rotate(x,y,rot,mx,my,w,flip,scale) local step = 1/16 scale=scale or 1 rot=rot\step * step local halfw, cx=scale*-w/2, mx + .5 local cy,cs,ss=my-halfw/scale,cos(rot)/scale,sin(rot)/scale local sx, sy, hx, hy=cx+cs*halfw, cy+ss*halfw, w*(flip and -4 or 4)*scale, w*4*scale for py=y-hy, y+hy do tline(x-hx, py, x+hx, py, sx -ss*halfw, sy + cs*halfw, cs/8, ss/8) halfw+=.125 end end |
Liek this you get 22.5, 45, etc...
still there is some intrinsic jagginess that sometimes depends on the sprite (I got the rocket from the cart and it was not symmetrical, so I fixed it here). For r-type homing missiles I had a problem when at some angles they looked weird when horizontal, so I drew them with an equal but opposite offset and they appear straight when rotated

You can also add a bit of manual anti-aliasing when drawing the sprite to hide it a bit (bottom rocket), but for real-time smoothing or dithering I have no idea.


@TheRoboZ in the code of the above cart you have:
if (not(o and (rot<.25 or rot >.75) and py>y+4))
What is this for?
It was causing issues for me, taking it out solved them, but I was just wondering what it's purpose is...
(Also note that is a text "o" and not a numeric 0)
[Please log in to post a comment]