Kaleidoscope (optimized)
This example was originally written by Scott Brosious, while it worked the method it used would compute each pixel multiple times. Making the effect much slower than need be.
This example includes three revisions of the original demo there's a tweaked version of the original code, plus an inverted version of the that. The inverted version is about twice as fast, but still uses the same brute force method.
The last version flips the problem upside down so it computes the distance and angle from the center for each unique pixel, then pulls the rotated angle from a displacement table and gets the colour. The result is around 9->10 times faster than the original on my system.
Related Articles:
* A Crash Course In BASIC program Optimization (http://www.underwaredesign.com/forums/index.php?topic=2548.0)
* Drawing A Pie Chart (http://www.underwaredesign.com/forums/index.php?topic=4090.0)
i don't get anything with mode 3
Kaleidoscope (optimized #2)
This version has one extra mode which pre-computes most of the heavy lifting as well as tweaks to the other methods.
Note: requires the media from the first post.
[pbcode]
; -----------------------------------------------------------------------------
; --[ABOUT]--------------------------------------------------------------------
; -----------------------------------------------------------------------------
; This example was originally written by Scott Brosious, while it worked
; the method it used would compute each pixel multiple times. Making the effect
; much slower than need be. This version includes three revisions of the demo
; there's a tweaked version of the original, plus an inverted version of the that.
; The inverted version is about twice as fast, but still uses the same brute force
; method. The last version flips the problem upside down so it computes the
; distance and angle from the center for each unique pixel, then pulls the rotated
; angle from a displacement table and gets the colour. The result is around
; 9->10 times faster than the original on my system.
;
; Have fun,
; Kevin Picone
; UnderwareDesign.com PlayBASIC.com
; -----------------------------------------------------------------------------
Loadfont "verdana",1,14
LoadFxImage "test01.png",1
XSize = 512
YSize = 512
global MaxRadius = XSize / 2
Dim Colours(3600,MaxRadius)
; Note to self: You can read the image before you clear the screen.
Print "loading "
sync
rendertoimage 1
CenterX=Xsize/2
CenterY=YSise/2
For AngleLP = 0 To 3600
ca#= Cos(Angle#)
ca#= Sin(Angle#)
Angle#=Anglelp/10.0
lockbuffer
For Radius = 0 To MaxRadius - 1
XScreen# = Radius * ca#
YScreen# = Radius * sa#
Colours(Angle#,Radius) = Point(CenterX + XScreen#,CenterY + YScreen#)
Next
unlockbuffer
Next
rendertoscreen
; Here is where you can come back to after an update.
// This table is used to compute the angles of two of the quadrants
Dim DirectionTable(90)
rendermode =2
; --------------------------------------------------------------------------
Do ; ------ [Main Loop ]
; --------------------------------------------------------------------------
Cls Rgb(0,0,0)
if SpaceKey()
RenderMode++
if RenderMode>3 then RenderMode=0
frames=0
tt#=0
flushkeys
endif
StartTime=Timer()
Select RenderMode
; -------------------------------------------
case 0
; -------------------------------------------
MEthodname$=Kaleidoscope_ORIGINAL(Twist#)
; -------------------------------------------
case 1
; -------------------------------------------
MEthodname$=Kaleidoscope_Inverted_Loops(Twist#)
; -------------------------------------------
case 2
; -------------------------------------------
MEthodname$=Kaleidoscope_Single_Pass_Version(Twist#)
; -------------------------------------------
case 3
; -------------------------------------------
MEthodname$=Kaleidoscope_Single_Pass_Cache_Scanline_Version(Twist#)
EndSelect
tt#+=Timer()-StartTime
; --------------------------------------------------------
; Display render time info
; --------------------------------------------------------
Frames++
print " Render Method #"+str$(RenderMode+1)+" of 4 [ "+MethodName$+" ]"
print " Twist Angle:"+str$(Twist#)
print " Render Time In Ticks:"+str$(tt#/frames)
print " Space to swap methods"
Sync
Twist# = WrapAngle(Twist#,10)
Loop esckey()
end
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; >> Render Kaleidoscope
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
;
; This is the original routine with a few tweaks, it's main problem is it's
; it's potentially rendering each pixel more than once.
Psub Kaleidoscope_ORIGINAL(Twist#)
XScreenMiddle = GetScreenWidth() / 2
YScreenMiddle = GetScreenHeight() / 2
Twist#=wrapangle(Twist#)
For Radius = 0 To MaxRadius - 1
RotateAngle = 0
For PieceOfPie = 1 To 4
AngularIncrement# = Twist#
lockbuffer
ThisRGB=point(0,0)
For Angle# = RotateAngle To RotateAngle + 45 Step 0.1
XScreen# = Radius * Cos(Angle#)
YScreen# = Radius * Sin(Angle#)
DotC XScreenMiddle + XScreen#,YScreenMiddle + YScreen#,Colours(AngularIncrement#,Radius)
AngularIncrement# = WrapAngle(AngularIncrement#,0.1)
Next Angle#
AngularDecrement# = Twist# + 45
RotateAngle = RotateAngle + 45
For Angle# = RotateAngle To RotateAngle + 45 Step 0.1
XScreen# = Radius * Cos(Angle#)
YScreen# = Radius * Sin(Angle#)
DotC XScreenMiddle + XScreen#,YScreenMiddle + YScreen#,Colours(AngularDecrement#,Radius)
AngularDecrement# = WrapAngle(AngularDecrement#,-0.1)
Next Angle#
unLockbuffer
RotateAngle = RotateAngle + 45
Next PieceOfPie
Next Radius
Method$= "Original Brute Force"
EndPsub Method$
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
;
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
Psub Kaleidoscope_Inverted_Loops(Twist#)
XScreenMiddle = GetScreenWidth() / 2
YScreenMiddle = GetScreenHeight() / 2
Twist#=wrapangle(Twist#)
RotateAngle = 0
For PieceOfPie = 1 To 4
AngularIncrement# = Twist#
lockbuffer
ThisRGB=point(0,0)
; For Angle# = RotateAngle To RotateAngle + 45 Step 0.1
For AngleLp = RotateAngle*10 To (RotateAngle + 45)*10
Angle#=Anglelp/10.0
ca#=Cos(Angle#)
sa#=sin(Angle#)
AngularIncrementInt=AngularIncrement#
For Radius = 0 To MaxRadius - 1
XScreen = Radius * ca#
YScreen = Radius * sa#
Dotc XScreenMiddle + XScreen,YScreenMiddle + YScreen,Colours(AngularIncrementInt,Radius)
Next Radius
AngularIncrement# = WrapAngle(AngularIncrement#,0.1)
Next AngleLP
AngularDecrement# = Twist# + 45
RotateAngle = RotateAngle + 45
For AngleLp = RotateAngle*10 To (RotateAngle + 45)*10
Angle#=Anglelp/10.0
ca#=Cos(Angle#)
sa#=sin(Angle#)
AngularDecrementInt=AngularDecrement#
For Radius = 0 To MaxRadius - 1
XScreen = Radius * ca#
YScreen = Radius * sa#
Dotc XScreenMiddle + XScreen,YScreenMiddle + YScreen,Colours(AngularDecrementInt,Radius)
Next Radius
AngularDecrement# = WrapAngle(AngularDecrement#,-0.1)
Next AngleLP
unLockbuffer
RotateAngle = RotateAngle + 45
Next PieceOfPie
Method$= "Inverted Brute Force"
EndPsub Method$
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; >> Kaleidoscope_Single_Pass_Version <<
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
;
; This version of the effect scan the fill region and computes the radius and
; angle to the center of the effect so it only renders each pixel once.
;
; -----------------------------------------------------------------------------
Psub Kaleidoscope_Single_Pass_Version(Twist#)
XScreenMiddle = GetScreenWidth() / 2
YScreenMiddle = GetScreenHeight() / 2
YScreenMiddle2 =YScreenMiddle+MaxRadius
Twist#=wrapangle(twist#)
For Anglelp=0 to 45
Angle#=Anglelp
DirectionTable(Anglelp)=wrapangle(angle#+twist#)
next
For Anglelp=45 to 90
Angle#=90-Anglelp
DirectionTable(Anglelp)=wrapangle(angle#+twist#)
next
MaxRadiusMinusOne=MaxRadius-1
lockbuffer
ThisRGB=Point(0,0)
For ylp= 0 to MaxRadiusMinusOne
For xlp= 0 to MaxRadiusMinusOne
Dist=GetDistance2d(0,0,xlp,ylp)
if Dist<=MaxRadius
AngleToPixel=GetAngle2d(0,0,xlp,ylp)
Angle =DirectionTable(AngleToPixel)
ThisColour=Colours(Angle,Dist)
Ypos=YScreenMiddle + Ylp
fastDot XScreenMiddle + xlp,Ypos ,ThisColour
fastDot XScreenMiddle - xlp,Ypos ,ThisColour
Ypos=YScreenMiddle - Ylp
fastDot XScreenMiddle + xlp,Ypos ,ThisColour
fastDot XScreenMiddle - xlp,Ypos ,ThisColour
endif
next
next
unlockbuffer
Method$ ="Single Pass Version"
EndPsub Method$
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; >> Kaleidoscope_Single_Pass_With Cached Scanlines Version <<
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
;
; This version caches each scan line and renders them as image strips, it also
; pre-computes the radius and angle tables. Making it about 40% faster than the
; previous single pass version.
;
; -----------------------------------------------------------------------------
Psub Kaleidoscope_Single_Pass_Cache_Scanline_Version(Twist#)
XScreenMiddle = GetScreenWidth() / 2
YScreenMiddle = GetScreenHeight() / 2
YScreenMiddle2 =YScreenMiddle+MaxRadius
Twist#=wrapangle(twist#)
For Anglelp=0 to 45
Angle#=Anglelp
DirectionTable(Anglelp)=wrapangle(angle#+twist#)
next
For Anglelp=45 to 90
Angle#=90-Anglelp
DirectionTable(Anglelp)=wrapangle(angle#+twist#)
next
MaxRadiusMinusOne=MaxRadius-1
// create scan line cache and pre-compute the distance and angle tables
if ScanlineBuffer=0
ScanLineBuffer=NewFXImage((maxradius*2),1)
Dim DistanceTable(MaxRadius*MaxRadius)
Dim AngleTable(MaxRadius*MaxRadius)
For ylp= 0 to MaxRadiusMinusOne
For xlp= 0 to MaxRadiusMinusOne
pos=ylp*MaxRadius+xlp
DistanceTable(pos) =GetDistance2d(0,0,xlp,ylp)
AngleTable(pos) =GetAngle2d(0,0,xlp,ylp)
next
next
endif
Xpos=XscreenMiddle-MaxRadius
For ylp= 0 to MaxRadiusMinusOne
rendertoimage ScanLineBuffer
cls 0
lockbuffer
ThisRGB=Point(0,0)
RowYPos=Ylp*MaxRAdius
For xlp= RowYpos to RowYpos+MaxRadiusMinusOne
Dist=DistanceTable(xlp)
if Dist<=MaxRadius
AngleToPixel=AngleTable(xlp)
Angle =DirectionTable(AngleToPixel)
ThisColour=Colours(Angle,Dist)
Xlp2=Xlp-RowYpos
fastDot maxradius + xlp2,0 ,ThisColour
fastDot maxradius - xlp2,0 ,ThisColour
endif
next
unlockbuffer
rendertoscreen
lockbuffer
drawimage ScanLineBuffer,Xpos,YScreenMiddle + Ylp,false
drawimage ScanLineBuffer,Xpos,YScreenMiddle - Ylp,false
unlockbuffer
next
Done:
Method$ ="Single Pass Version with cached strips"
EndPsub Method$
[/pbcode]
woo number 4 is speeedy!
Kaleidoscope (optimized #3)
And here's one last tiny change (mode #5) to show the thought progression. The 5th version just changes the table data so that the range of the data is from 1 to MaxRadius. This means we can remove a compare operation from the inner loop. Might not seem like much, but since the inner loop is running 1000's of times, this wins us back another few milliseconds across the effect. You could remove it entirely by computing the span width for each scan line just like you would when drawing a filled circle. Could probably pack the table data differently to push further also..
[pbcode]
; -----------------------------------------------------------------------------
; --[ABOUT]--------------------------------------------------------------------
; -----------------------------------------------------------------------------
; This example was originally written by Scott Brosious, while it worked
; the method it used would compute each pixel multiple times. Making the effect
; much slower than need be. This version includes three revisions of the demo
; there's a tweaked version of the original, plus an inverted version of the that.
; The inverted version is about twice as fast, but still uses the same brute force
; method. The last version flips the problem upside down so it computes the
; distance and angle from the center for each unique pixel, then pulls the rotated
; angle from a displacement table and gets the colour. The result is around
; 9->10 times faster than the original on my system.
;
; Have fun,
; Kevin Picone
; UnderwareDesign.com PlayBASIC.com
; -----------------------------------------------------------------------------
Loadfont "verdana",1,14
LoadFxImage "test01.png",1
XSize = 512
YSize = 512
global MaxRadius = XSize / 2
Dim Colours(3600,MaxRadius)
Dim Colours2(3600,MaxRadius+1) ; Only but by the 5th version of the routine
; Note to self: You can read the image before you clear the screen.
Print "loading "
sync
rendertoimage 1
CenterX=Xsize/2
CenterY=YSise/2
For AngleLP = 0 To 3600
Angle#=Anglelp/10.0
ca#= Cos(Angle#)
ca#= Sin(Angle#)
lockbuffer
For Radius = 0 To MaxRadius - 1
XScreen# = Radius * ca#
YScreen# = Radius * sa#
ThisRGB=Point(CenterX + XScreen#,CenterY + YScreen#)
Colours(Angle#,Radius) = ThisRGB
Colours2(Angle#,Radius+1)=ThisRGB ; Colour table offset from 1 to maxradius
Next
unlockbuffer
Next
rendertoscreen
; Here is where you can come back to after an update.
// This table is used to compute the angles of two of the quadrants
Dim DirectionTable(90)
rendermode =2
; --------------------------------------------------------------------------
Do ; ------ [Main Loop ]
; --------------------------------------------------------------------------
Cls Rgb(0,0,0)
if SpaceKey()
RenderMode++
if RenderMode>4 then RenderMode=0
frames=0
tt#=0
flushkeys
endif
StartTime=Timer()
Select RenderMode
; --------------------------------------------------------------
case 0
; --------------------------------------------------------------
MEthodname$=Kaleidoscope_ORIGINAL(Twist#)
; --------------------------------------------------------------
case 1
; --------------------------------------------------------------
MEthodname$=Kaleidoscope_Inverted_Loops(Twist#)
; --------------------------------------------------------------
case 2
; --------------------------------------------------------------
MEthodname$=Kaleidoscope_Single_Pass_Version(Twist#)
; --------------------------------------------------------------
case 3
; --------------------------------------------------------------
MEthodname$=Kaleidoscope_Single_Pass_Cache_Scanline_Version(Twist#)
; --------------------------------------------------------------
case 4
; --------------------------------------------------------------
MEthodname$=Kaleidoscope_Single_Pass_Cache_Scanline_Version2(Twist#)
EndSelect
tt#+=Timer()-StartTime
; --------------------------------------------------------
; Display render time info
; --------------------------------------------------------
Frames++
print " Render Method #"+str$(RenderMode+1)+" of 5 [ "+MethodName$+" ]"
print " Twist Angle:"+str$(Twist#)
print " Render Time In Ticks:"+str$(tt#/frames)
print " Space to swap methods"
Sync
Twist# = WrapAngle(Twist#,10)
Loop esckey()
end
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; >> Render Kaleidoscope
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
;
; This is the original routine with a few tweaks, it's main problem is it's
; it's potentially rendering each pixel more than once.
Psub Kaleidoscope_ORIGINAL(Twist#)
XScreenMiddle = GetScreenWidth() / 2
YScreenMiddle = GetScreenHeight() / 2
Twist#=wrapangle(Twist#)
For Radius = 0 To MaxRadius - 1
RotateAngle = 0
For PieceOfPie = 1 To 4
AngularIncrement# = Twist#
lockbuffer
ThisRGB=point(0,0)
For Angle# = RotateAngle To RotateAngle + 45 Step 0.1
XScreen# = Radius * Cos(Angle#)
YScreen# = Radius * Sin(Angle#)
DotC XScreenMiddle + XScreen#,YScreenMiddle + YScreen#,Colours(AngularIncrement#,Radius)
AngularIncrement# = WrapAngle(AngularIncrement#,0.1)
Next Angle#
AngularDecrement# = Twist# + 45
RotateAngle = RotateAngle + 45
For Angle# = RotateAngle To RotateAngle + 45 Step 0.1
XScreen# = Radius * Cos(Angle#)
YScreen# = Radius * Sin(Angle#)
DotC XScreenMiddle + XScreen#,YScreenMiddle + YScreen#,Colours(AngularDecrement#,Radius)
AngularDecrement# = WrapAngle(AngularDecrement#,-0.1)
Next Angle#
unLockbuffer
RotateAngle = RotateAngle + 45
Next PieceOfPie
Next Radius
Method$= "Original Brute Force"
EndPsub Method$
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
;
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
Psub Kaleidoscope_Inverted_Loops(Twist#)
XScreenMiddle = GetScreenWidth() / 2
YScreenMiddle = GetScreenHeight() / 2
Twist#=wrapangle(Twist#)
RotateAngle = 0
For PieceOfPie = 1 To 4
AngularIncrement# = Twist#
lockbuffer
ThisRGB=point(0,0)
; For Angle# = RotateAngle To RotateAngle + 45 Step 0.1
For AngleLp = RotateAngle*10 To (RotateAngle + 45)*10
Angle#=Anglelp/10.0
ca#=Cos(Angle#)
sa#=sin(Angle#)
AngularIncrementInt=AngularIncrement#
For Radius = 0 To MaxRadius - 1
XScreen = Radius * ca#
YScreen = Radius * sa#
Dotc XScreenMiddle + XScreen,YScreenMiddle + YScreen,Colours(AngularIncrementInt,Radius)
Next Radius
AngularIncrement# = WrapAngle(AngularIncrement#,0.1)
Next AngleLP
AngularDecrement# = Twist# + 45
RotateAngle = RotateAngle + 45
For AngleLp = RotateAngle*10 To (RotateAngle + 45)*10
Angle#=Anglelp/10.0
ca#=Cos(Angle#)
sa#=sin(Angle#)
AngularDecrementInt=AngularDecrement#
For Radius = 0 To MaxRadius - 1
XScreen = Radius * ca#
YScreen = Radius * sa#
Dotc XScreenMiddle + XScreen,YScreenMiddle + YScreen,Colours(AngularDecrementInt,Radius)
Next Radius
AngularDecrement# = WrapAngle(AngularDecrement#,-0.1)
Next AngleLP
unLockbuffer
RotateAngle = RotateAngle + 45
Next PieceOfPie
Method$= "Inverted Brute Force"
EndPsub Method$
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; >> Kaleidoscope_Single_Pass_Version <<
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
;
; This version of the effect scan the fill region and computes the radius and
; angle to the center of the effect so it only renders each pixel once.
;
; -----------------------------------------------------------------------------
Psub Kaleidoscope_Single_Pass_Version(Twist#)
XScreenMiddle = GetScreenWidth() / 2
YScreenMiddle = GetScreenHeight() / 2
YScreenMiddle2 =YScreenMiddle+MaxRadius
Twist#=wrapangle(twist#)
For Anglelp=0 to 45
Angle#=Anglelp
DirectionTable(Anglelp)=wrapangle(angle#+twist#)
next
For Anglelp=45 to 90
Angle#=90-Anglelp
DirectionTable(Anglelp)=wrapangle(angle#+twist#)
next
MaxRadiusMinusOne=MaxRadius-1
lockbuffer
ThisRGB=Point(0,0)
For ylp= 0 to MaxRadiusMinusOne
For xlp= 0 to MaxRadiusMinusOne
Dist=GetDistance2d(0,0,xlp,ylp)
if Dist<=MaxRadius
AngleToPixel=GetAngle2d(0,0,xlp,ylp)
Angle =DirectionTable(AngleToPixel)
ThisColour=Colours(Angle,Dist)
Ypos=YScreenMiddle + Ylp
fastDot XScreenMiddle + xlp,Ypos ,ThisColour
fastDot XScreenMiddle - xlp,Ypos ,ThisColour
Ypos=YScreenMiddle - Ylp
fastDot XScreenMiddle + xlp,Ypos ,ThisColour
fastDot XScreenMiddle - xlp,Ypos ,ThisColour
endif
next
next
unlockbuffer
Method$ ="Single Pass Version"
EndPsub Method$
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; >> Kaleidoscope_Single_Pass_With Cached Scanlines Version <<
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
;
; This version caches each scan line and renders them as image strips, it also
; pre-computes the radius and angle tables. Making it about 40% faster than the
; previous single pass version.
;
; -----------------------------------------------------------------------------
Psub Kaleidoscope_Single_Pass_Cache_Scanline_Version(Twist#)
XScreenMiddle = GetScreenWidth() / 2
YScreenMiddle = GetScreenHeight() / 2
YScreenMiddle2 =YScreenMiddle+MaxRadius
Twist#=wrapangle(twist#)
For Anglelp=0 to 45
Angle#=Anglelp
DirectionTable(Anglelp)=wrapangle(angle#+twist#)
next
For Anglelp=45 to 90
Angle#=90-Anglelp
DirectionTable(Anglelp)=wrapangle(angle#+twist#)
next
MaxRadiusMinusOne=MaxRadius-1
// create scan line cache and pre-compute the distance and angle tables
if ScanlineBuffer=0
ScanLineBuffer=NewFXImage((maxradius*2),1)
Dim DistanceTable(MaxRadius*MaxRadius)
Dim AngleTable(MaxRadius*MaxRadius)
For ylp= 0 to MaxRadiusMinusOne
For xlp= 0 to MaxRadiusMinusOne
pos=ylp*MaxRadius+xlp
DistanceTable(pos) =GetDistance2d(0,0,xlp,ylp)
AngleTable(pos) =GetAngle2d(0,0,xlp,ylp)
next
next
endif
Xpos=XscreenMiddle-MaxRadius
For ylp= 0 to MaxRadiusMinusOne
rendertoimage ScanLineBuffer
cls 0
lockbuffer
ThisRGB=Point(0,0)
RowYPos=Ylp*MaxRAdius
For xlp= RowYpos to RowYpos+MaxRadiusMinusOne
Dist=DistanceTable(xlp)
if Dist<=MaxRadius
AngleToPixel=AngleTable(xlp)
Angle =DirectionTable(AngleToPixel)
ThisColour=Colours(Angle,Dist)
Xlp2=Xlp-RowYpos
fastDot maxradius + xlp2,0 ,ThisColour
fastDot maxradius - xlp2,0 ,ThisColour
endif
next
unlockbuffer
rendertoscreen
lockbuffer
drawimage ScanLineBuffer,Xpos,YScreenMiddle + Ylp,false
drawimage ScanLineBuffer,Xpos,YScreenMiddle - Ylp,false
unlockbuffer
next
Done:
Method$ ="Single Pass Version with cached strips"
EndPsub Method$
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; >> Kaleidoscope_Single_Pass_With Cached Scanlines Version <<
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
; -----------------------------------------------------------------------------
;
; This removes the compare from the version caches each scan line and renders them as image strips, it also
; pre-computes the radius and angle tables. Making it about 40% faster than the
; previous single pass version.
;
; -----------------------------------------------------------------------------
Psub Kaleidoscope_Single_Pass_Cache_Scanline_Version2(Twist#)
XScreenMiddle = GetScreenWidth() / 2
YScreenMiddle = GetScreenHeight() / 2
YScreenMiddle2 =YScreenMiddle+MaxRadius
Twist#=wrapangle(twist#)
For Anglelp=0 to 45
Angle#=Anglelp
DirectionTable(Anglelp)=wrapangle(angle#+twist#)
next
For Anglelp=45 to 90
Angle#=90-Anglelp
DirectionTable(Anglelp)=wrapangle(angle#+twist#)
next
MaxRadiusMinusOne=MaxRadius-1
// create scan line cache and pre-compute the distance and angle tables
if ScanlineBuffer=0
ScanLineBuffer=NewFXImage((maxradius*2),1)
Dim DistanceTable(MaxRadius*MaxRadius)
Dim AngleTable(MaxRadius*MaxRadius)
For ylp= 0 to MaxRadiusMinusOne
For xlp= 0 to MaxRadiusMinusOne
Dist=GetDistance2d(0,0,xlp,ylp)
if Dist<=MaxRadius
pos=ylp*MaxRadius+xlp
DistanceTable(pos) =Dist+1
AngleTable(pos) =GetAngle2d(0,0,xlp,ylp)
endif
next
next
endif
Xpos=XscreenMiddle-MaxRadius
For ylp= 0 to MaxRadiusMinusOne
rendertoimage ScanLineBuffer
cls 0
lockbuffer
ThisRGB=Point(0,0)
RowYPos=Ylp*MaxRAdius
For xlp= RowYpos to RowYpos+MaxRadiusMinusOne
Dist=DistanceTable(xlp)
; here we've removed the need for the compare, since Dist
; is now offset from 1 to MaxRadius, so it'll return zero
; when outside the circle.
if Dist
AngleToPixel=AngleTable(xlp)
Angle =DirectionTable(AngleToPixel)
ThisColour =Colours2(Angle,Dist)
Xlp2=Xlp-RowYpos
fastDot maxradius + xlp2,0 ,ThisColour
fastDot maxradius - xlp2,0 ,ThisColour
endif
next
unlockbuffer
rendertoscreen
lockbuffer
drawimage ScanLineBuffer,Xpos,YScreenMiddle + Ylp,false
drawimage ScanLineBuffer,Xpos,YScreenMiddle - Ylp,false
unlockbuffer
next
Done:
Method$ ="Single Pass Version with cached strips Version #2"
EndPsub Method$
[/pbcode]
Kaleidoscope (optimized #4) This version is only slightly tweaked version of the previous snippets, the main difference is it's targeted at running on Windows 8 / Windows 10 which have trouble with direct draw emulation, namely they can slow execution down a huge amount. It generally manifests itself when trying to draw lots of small image fragments, which is used within this demo used in the fourth and fifth editions of the routine. To counter this, we just render everything to and FX screen and then draw that to the screen.
Video music by: https://BenSound.com
Download Attached