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) 🤔
ok ok I see where I can have a much leaner version. Stay tuned!
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.
That =IS= better, @TheRoboZ. I figured intelligent dithering would fix a lot of the jagginess. Well done.
@Xeonic it uses tline so it is reading from the map area. You need to put your sprite to be rotated in an area of the map editor. Check the updated cart and description above.
Sorry, there was a typo in this line:
local sx, sy = mx+cs-w, my+ss-w
you need to add half w to the starting my coordinate when calling the function and not inside the rotation code. Anyway see the updated cart
@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)
@mika76 sorry it is a leftover from how I was using it in a game cart that I forgot to remove, it’s not part of the rotation code
Hey everyone! I need help with math. 😦
I'm struggling trying to rotate and mirror(or flip) a sprite tile. I am using the code from TheRoboZ But am struggling to get the Y axis flipping as well as the X axis does.
Here is the code and an attached gif to show what I'm talking about.
rot=0/360 snek_head={x=30, y=20} dir=3 function pd_rotate(x,y,rot,mx,my,w,flp,scale) scale=scale or 1 w*=scale*4 halfw = -w local cs, ss = cos(rot)*.125/scale, sin(rot)*.125/scale local sx, sy = mx+cs*-w, my+ss*-w if dir>1 then -- left or right hx = flp and -w or w else -- up or down hx =-w end for py=y-w, y+w do --TLINE(X0, Y0, X1, Y1, MX, MY, [MDX, MDY], [LAYERS]) tline(x-hx, py, x+hx, py, sx-ss*halfw, sy+cs*halfw, cs, ss) halfw+=1 end print("hx" .. hx, 7) end function _draw() cls()-- 2=n180(2) 3=s360(0) 1=e270(3) 0=w90(1) if btnp(0) and dir ~= 1 then dir = 0 rot=3/4 elseif btnp(1) and dir ~= 0 then dir = 1 rot=1/4 elseif btnp(2) and dir ~= 3 then dir = 2 rot=2/4 elseif btnp(3) and dir ~= 2 then dir = 3 rot=0/4 end if dir==0 then snek_head.x-=1 elseif dir==1 then snek_head.x+=1 elseif dir==2 then snek_head.y-=1 elseif dir==3 then snek_head.y+=1 end if t()%2>1 then flp=true end if t()%2<1 then flp=false end pd_rotate(snek_head.x,snek_head.y, rot,4.5,8.5,1,flp,1) end |
I'm trying to accomplish a "wiggle" going left to right similar to the way the "wiggle" works top to bottom.
I think the problem lies somewhere in how I'm calling the tline but I'm unsure how.
Any math related solutions would be super appreciated! I'm not interested in just adding another sprite for this.
UPDATE::::::::::::::::::::::::
Follow up to my question with the updated solution thanks to @freds72 telling me to use SPR instead of the Rotate function
dir=3 snek_head={x=20, y=20} function _draw() cls()-- 2=n180(2) 3=s360(0) 1=e270(3) 0=w90(1) if btnp(0) and dir ~= 1 then dir = 0 elseif btnp(1) and dir ~= 0 then dir = 1 elseif btnp(2) and dir ~= 3 then dir = 2 elseif btnp(3) and dir ~= 2 then dir = 3 end if dir==0 then snek_head.x-=1 snek_head.n=18 flp_x=true elseif dir==1 then snek_head.x+=1 snek_head.n=18 flp_x=false elseif dir==2 then snek_head.y-=1 snek_head.n=17 flp_y=true elseif dir==3 then snek_head.y+=1 snek_head.n=17 flp_y=false end if dir>1 then--up/down if t()%1>.5 then flp_x=true else flp_x=false end end if dir<2 then--left/right if t()%1>.5 then flp_y=true else flp_y=false end end spr(snek_head.n, snek_head.x, snek_head.y,1,1,flp_x,flp_y) end |
[Please log in to post a comment]