Rotozoom without the skew - James D Jarvis - 01-19-2023
I was using rotozoom2 when I noticed it was skewing the image it was rotating when xscale and yscale were not identical values. (I also adjusted it to used degrees as opposed to radians, but that has nothing to do with the skew).
The change was in multiplying px(0) to px(3) and py(0) to py(3) by the scale factors prior to rotation.
Code: (Select All) Sub RotoZoom_jan23 (centerX As Long, centerY As Long, Image As Long, xScale As Single, yScale As Single, Rotation As Single)
Dim px(3) As Single: Dim py(3) As Single
W& = _Width(Image&): H& = _Height(Image&)
px(0) = -W& / 2 * xScale: py(0) = -H& / 2 * yScale: px(1) = -W& / 2 * xScale: py(1) = H& / 2 * yScale
px(2) = W& / 2 * xScale: py(2) = H& / 2 * yScale: px(3) = W& / 2 * xScale: py(3) = -H& / 2 * yScale
sinr! = Sin(-0.01745329 * Rotation): cosr! = Cos(-0.01745329 * Rotation)
For i& = 0 To 3
' x2& = (px(i&) * cosr! + sinr! * py(i&)) * xScale + centerX: y2& = (py(i&) * cosr! - px(i&) * sinr!) * yScale + centerY
x2& = (px(i&) * cosr! + sinr! * py(i&)) + centerX: y2& = (py(i&) * cosr! - px(i&) * sinr!) + centerY
px(i&) = x2&: py(i&) = y2&
Next
_MapTriangle (0, 0)-(0, H& - 1)-(W& - 1, H& - 1), Image& To(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
_MapTriangle (0, 0)-(W& - 1, 0)-(W& - 1, H& - 1), Image& To(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
End Sub
RE: Rotozoom without the skew - bplus - 01-19-2023
Yep! There is something definitely wrong with the old Rotozoom2 and 3, the Red Grid in my test demo.
The blue grid is holding the correct ratio of sides all the way around the circle, the red only lines up with correct ratio of sides every 90 degrees. Huh!
Very nice fix @James D Jarvis!
Here is my test code that includes the original images I tested. Guess I did not test everything like rotating with different scales on x and y.
RE: Rotozoom without the skew - James D Jarvis - 01-19-2023
I discovered it trying to use rotozoom and circlefill to draw ellipses. "Hey why's that ellipse bending?". Took me way too long messing with sin and cos until I realized how simple the solution was.
RE: Rotozoom without the skew - TerryRitchie - 01-19-2023
This rotozoom is much better than the one I currently use that was modified from Galleon's rotozoom posted way back on the original forum from 2010. I would like to incorporate it into my library and credit the authors. I just want to get this straight, this code was originally written by Bplus and then modified by James to fix the skew, correct? Would this be considered RotoZoom version 4?
Also, may I show this code on the tutorial in lesson 17: Movement and Rotation?
RE: Rotozoom without the skew - bplus - 01-19-2023
@TerryRitchie
I just did a mod of Galleon's Rotozoom from the example in Wiki. I get credit only for seeing that the x and y axis could be scaled independently and for not testing it enough! James J Jarvis gets credit for fixing that up to work correctly.
I can't imagine anyone objecting to trying to teach best methods to newbies. James D Jarvis also has nice advanced (IMHO) way to weight a random number around a center:
random = center + rnd(range) - rnd(range)
if I have it correctly. That's a keeper too! IMHO, unless it's more commonly known than I suspect.
RE: Rotozoom without the skew - bplus - 01-19-2023
BTW I am calling Jarvis fix RotoZoom23d because it's better than my 2 and 3 that used degrees 2 radians 3, the 23 for the year of fix and d to remind me it takes degrees not radians. Will another fix show up this year? It took several before this one arrived, version 2 was posted in Library of old, .org, forum long ago. There would be less math if radians were used but... eh 6 of 1, half dozen of another...
RE: Rotozoom without the skew - bplus - 01-20-2023
Before I add a routine to my toolbox of routines (Subs or Functions) I run the code in a testing /demo code I keep in a Folder call 000Test.
While updating my RotoZoom23d with James Javis fix, I noticed I had 2 differences between my Rotozoom2 and 3.
1. 3 used Radians while 2 used Degrees
2. 3 used Seamless while 2 didn't
I think _Seamless works better for images but for line drawing like the grid I tested with it, it missed an edge or 2.
BTW line drawings always suck in Rotozoom because of math rounding.
Anyway, you may or may not want to use _Seamless. Here is my test code using _Seamless with the grid because it missed an edge on grid more than NOT using _Seamless I would say it is slightly worse in this case with a line drawing. But I think for non line drawing images it is better... maybe @SMcNeill can weigh in?
Code: (Select All) _Title "Different Rotozooms test with grid" 'b+ 2023-01-18
' 01-20 seamless added to 23d
' with original tests of Rotozoom2 with independent scales for x and y axis
Const xmax = 800
Const ymax = 600
Dim Shared pi As _Float
pi = _Pi
Screen _NewImage(xmax, ymax, 32)
_ScreenMove 360, 60
Dim As Long imgD, imgR
imgD = _NewImage(801, 801, 32)
_Dest imgD
Color &HFF0000FF
drawGrid 0, 0, 20, 40, 40
imgR = _NewImage(801, 801, 32)
_Dest imgR
Color &HFFFF0000
drawGrid 0, 0, 20, 40, 40
_Dest 0
Dim r As _Float
Do
Cls
Color &HFF0000FF: Print d
Color &HFFFF0000: Print Int(_R2D(r))
'Color &HFFFFFFFF
RotoZoom23d 400, 300, imgD, .5, 1, d
RotoZoom2 400, 300, imgR, .5, 1, d
'RotoZoom3 400, 300, imgR, .5, 1, r
d = d + 1
r = _D2R(d)
_Display
_Limit 5
Loop Until _KeyDown(27)
' ==================== Test Code when orininally tested scaling Rotozooms =================================
' Rotozoom23 works fine here too!
backImage& = _LoadImage("Stars.png")
myImage& = _LoadImage("Starship.png")
xscale = .2: yscale = 1.4
''test shrink x expand x
For lc = 1 To 800 Step 5
_PutImage , backImage&
RotoZoom23d xmax / 2, ymax / 2, myImage&, (200 - lc) / 200 + .5, 1, 0
_Display
_Limit 20
Next
''test shrink and expand y
For lc = 1 To 800 Step 5
_PutImage , backImage&
RotoZoom23d xmax / 2, ymax / 2, myImage&, 1, (200 - lc) / 200 + .5, 0
_Display
_Limit 20
Next
''test rotation
For a = 0 To 360
_PutImage , backImage&
RotoZoom23d xmax / 2, ymax / 2, myImage&, 1, 1, a
_Display
_Limit 20
Next
'test warp drive
_PutImage , backImage&
For x = 10 To xmax
RotoZoom23d x, ymax / 2, myImage&, (1 + x / xmax) ^ 2, 1, 0
_Display
_Limit 2000
Next
Sub drawGrid (x, y, sq, xn, yn) ' top left x, y, x side, y side, number of x, nmber of y
Dim As Long i, dx, dy
dx = sq * xn: dy = sq * yn
For i = 0 To xn
Line (x + sq * i, y)-(x + sq * i, y + dy)
Next
For i = 0 To yn
Line (x, y + sq * i)-(x + dx, y + sq * i)
Next
End Sub
Sub RotoZoom2 (X As Long, Y As Long, Image As Long, xScale As Single, yScale, Rotation As Single)
Dim px(3) As Single: Dim py(3) As Single
W& = _Width(Image&): H& = _Height(Image&)
px(0) = -W& / 2: py(0) = -H& / 2: px(1) = -W& / 2: py(1) = H& / 2
px(2) = W& / 2: py(2) = H& / 2: px(3) = W& / 2: py(3) = -H& / 2
sinr! = Sin(-Rotation / 57.2957795131): cosr! = Cos(-Rotation / 57.2957795131)
For i& = 0 To 3
x2& = (px(i&) * cosr! + sinr! * py(i&)) * xScale + X: y2& = (py(i&) * cosr! - px(i&) * sinr!) * yScale + Y
px(i&) = x2&: py(i&) = y2&
Next
_MapTriangle (0, 0)-(0, H& - 1)-(W& - 1, H& - 1), Image& To(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
_MapTriangle (0, 0)-(W& - 1, 0)-(W& - 1, H& - 1), Image& To(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
End Sub
Sub RotoZoom3 (X As Long, Y As Long, Image As Long, xScale As Single, yScale As Single, radianRotation As Single) ' 0 at end means no scaling of x or y
Dim px(3) As Single: Dim py(3) As Single
Dim W&, H&, sinr!, cosr!, i&, x2&, y2&
W& = _Width(Image&): H& = _Height(Image&)
px(0) = -W& / 2: py(0) = -H& / 2: px(1) = -W& / 2: py(1) = H& / 2
px(2) = W& / 2: py(2) = H& / 2: px(3) = W& / 2: py(3) = -H& / 2
sinr! = Sin(-radianRotation): cosr! = Cos(-radianRotation)
For i& = 0 To 3
x2& = xScale * (px(i&) * cosr! + sinr! * py(i&)) + X: y2& = yScale * (py(i&) * cosr! - px(i&) * sinr!) + Y
px(i&) = x2&: py(i&) = y2&
Next
_MapTriangle _Seamless(0, 0)-(0, H& - 1)-(W& - 1, H& - 1), Image To(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
_MapTriangle _Seamless(0, 0)-(W& - 1, 0)-(W& - 1, H& - 1), Image To(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
End Sub
Sub RotoZoom23 (centerX As Long, centerY As Long, Image As Long, xScale As Single, yScale As Single, Rotation As Single)
Dim px(3) As Single: Dim py(3) As Single
W& = _Width(Image&): H& = _Height(Image&)
px(0) = -W& / 2 * xScale: py(0) = -H& / 2 * yScale: px(1) = -W& / 2 * xScale: py(1) = H& / 2 * yScale
px(2) = W& / 2 * xScale: py(2) = H& / 2 * yScale: px(3) = W& / 2 * xScale: py(3) = -H& / 2 * yScale
sinr! = Sin(-0.01745329 * Rotation): cosr! = Cos(-0.01745329 * Rotation)
For i& = 0 To 3
' x2& = (px(i&) * cosr! + sinr! * py(i&)) * xScale + centerX: y2& = (py(i&) * cosr! - px(i&) * sinr!) * yScale + centerY
x2& = (px(i&) * cosr! + sinr! * py(i&)) + centerX: y2& = (py(i&) * cosr! - px(i&) * sinr!) + centerY
px(i&) = x2&: py(i&) = y2&
Next
_MapTriangle (0, 0)-(0, H& - 1)-(W& - 1, H& - 1), Image& To(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
_MapTriangle (0, 0)-(W& - 1, 0)-(W& - 1, H& - 1), Image& To(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
End Sub
' tested in same rotozoom23d test code as original 2023-01-20
Sub RotoZoom23d (centerX As Long, centerY As Long, Image As Long, xScale As Single, yScale As Single, DRotation As Single)
Dim As Single px(3), py(3), sinr, cosr ' thanks to James D Jarvis who fixed this on 2023/01/18
Dim As Long IW, IH, i, x2, y2
IW& = _Width(Image&): IH& = _Height(Image&)
px(0) = -IW& / 2 * xScale: py(0) = -IH& / 2 * yScale: px(1) = -IW& / 2 * xScale: py(1) = IH& / 2 * yScale
px(2) = IW& / 2 * xScale: py(2) = IH& / 2 * yScale: px(3) = IW& / 2 * xScale: py(3) = -IH& / 2 * yScale
sinr! = Sin(-0.01745329 * DRotation): cosr! = Cos(-0.01745329 * DRotation)
For i& = 0 To 3
' x2& = (px(i&) * cosr! + sinr! * py(i&)) * xScale + centerX: y2& = (py(i&) * cosr! - px(i&) * sinr!) * yScale + centerY
x2& = (px(i&) * cosr! + sinr! * py(i&)) + centerX: y2& = (py(i&) * cosr! - px(i&) * sinr!) + centerY
px(i&) = x2&: py(i&) = y2&
Next
' might not need Seamless?
_MapTriangle _Seamless(0, 0)-(0, IH& - 1)-(IW& - 1, IH& - 1), Image& To(px(0), py(0))-(px(1), py(1))-(px(2), py(2))
_MapTriangle _Seamless(0, 0)-(IW& - 1, 0)-(IW& - 1, IH& - 1), Image& To(px(0), py(0))-(px(3), py(3))-(px(2), py(2))
End Sub
RE: Rotozoom without the skew - OldMoses - 01-21-2023
A very interesting thread that got me to investigate how Rotozoom actually works. An extra slice of raisin pie to Mr. Jarvis (as they used to say in Easyriders mag) for getting it fixed. I never noticed the issue since I never had occasion to use different scale factors.
While picking the SUB apart I noticed that I could tweak it to work with various parts of my typical vector library scheme and my own idiosyncrasies. Here's the work in progress that I've been playing with:
Code: (Select All) TYPE V2
x AS SINGLE
y AS SINGLE
END TYPE
DIM SHARED img&
DIM sp AS V2
SCREEN _NEWIMAGE(1024, 512, 32)
img& = _NEWIMAGE(256, 256, 32)
MakeImage
deg1! = _D2R(1)
sx! = 1 ' X Scale factor
sy! = 1 ' Y Scale factor
redo:
sp.x = 128: sp.y = 256
FOR x% = 0 TO 360
CLS
LINE (0, _HEIGHT * (3 / 4))-(_WIDTH - 1, _HEIGHT * (3 / 4)), &HFFFFFFFF
sp.x = sp.x + (SIN(deg1!) * 128)
sp.y = _HEIGHT * (3 / 4) - (_HEIGHT(img&) / 2) - ABS(COS(_D2R(x%)) * (sy! * 128 - 128))
'sin0=0 sin90=1 sin180=0 sin270=-1
'cos0=1 cos90=0 cos180=-1 cos270=0
RotoZoomV sp, img&, sx!, sy!, x%
_LIMIT 80
_DISPLAY
NEXT x%
INPUT "again? ", a$
IF a$ = "y" THEN GOTO redo
END
SUB RotoZoomV (center AS V2, Image AS LONG, xScale AS SINGLE, yScale AS SINGLE, Rotation AS SINGLE)
'Development progression: Galleon/Bplus/James Jarvis/altered for OldMoses' vector libraries
DIM AS V2 p(3)
W& = _WIDTH(Image): WS& = W& / 2 * xScale
H& = _HEIGHT(Image): HS& = H& / 2 * yScale
rota! = -0.01745329 * Rotation ' convert degrees to radians
FOR i& = 0 TO 3
p(i&).x = WS& * (1 + 2 * (i& < 2)) ' i& < 2 return -1 else return 1
p(i&).y = HS& * (1 + 2 * (i& MOD 3 = 0)) ' i& = 0 or 3 return -1 else return 1
R2_Roto p(i&), rota! ' perform rotation transform
R2_Add p(i&), center, 1 ' add center postition to result
NEXT
_MAPTRIANGLE (0, 0)-(0, H& - 1)-(W& - 1, H& - 1), Image TO(p(0).x, p(0).y)-(p(1).x, p(1).y)-(p(2).x, p(2).y)
_MAPTRIANGLE (0, 0)-(W& - 1, 0)-(W& - 1, H& - 1), Image TO(p(0).x, p(0).y)-(p(3).x, p(3).y)-(p(2).x, p(2).y)
END SUB 'RotoZoomV
SUB R2_Roto (v AS V2, ang AS SINGLE) 'transform v, rotating by ang radians
s! = SIN(ang)
c! = COS(ang)
x! = v.x: y! = v.y
v.x = x! * c! + y! * s!
v.y = y! * c! - x! * s!
END SUB 'R2_RotZ
SUB R2_Add (v AS V2, s AS V2, scalar AS SINGLE)
v.x = v.x + s.x * scalar
v.y = v.y + s.y * scalar
END SUB 'R2_Add
SUB MakeImage
_DEST img&
x% = _WIDTH: y% = _HEIGHT: x2% = _SHR(x%, 1): y2% = _SHR(y%, 1)
COLOR , &H00000000
CLS
LINE (x2%, y2%)-(x2%, 0), &HFFFFFFFF
LINE (x2%, y2%)-(x% - 1, y2%), &HFFFF0000
LINE (x2%, y2%)-(x2%, y% - 1), &HFF00FF00
LINE (x2%, y2%)-(0, y2%), &HFF0000FF
CIRCLE (x2%, y2%), 127
_DEST 0
END SUB 'MakeImage
RE: Rotozoom without the skew - SMcNeill - 01-21-2023
I'll have to check later to see if my DisplayImage suffers from this same skew problem. https://staging.qb64phoenix.com/showthread.php?tid=133
RE: Rotozoom without the skew - James D Jarvis - 01-21-2023
Is there a performance hit on using _seamless? Using it seems to clean up the pixel shift that sometimes shows up in the image (apart form the skew issue that was cleaned up).
|