Rotozoom without the skew
#1
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
Reply
#2
Thumbs Up 
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.


Attached Files
.zip   Test Different RotoZooms.zip (Size: 736.54 KB / Downloads: 21)
b = b + ...
Reply
#3
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.
Reply
#4
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?
Reply
#5
@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.
b = b + ...
Reply
#6
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...
b = b + ...
Reply
#7
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
b = b + ...
Reply
#8
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
DO: LOOP: DO: LOOP
sha_na_na_na_na_na_na_na_na_na:
Reply
#9
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
Reply
#10
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).
Reply




Users browsing this thread: 9 Guest(s)