Zooming smoothly

BlitzMax Forums/BlitzMax Programming/Zooming smoothly

The forum is locked. These are the original blitzbasic.com forums, kept so they can still be read. No new topics, replies or edits can be made here. To join in, head over to the Blitz3D+ forums.

Ok, I had some time to mess with your code and found it doesn't work. It looks fine when only the X is being drawn, but when you're zooming in with all kinds of things drawn around the outside, you notice a definite deceleration.

i thought about it for a while and realized something. If you stretch the world (or shrink the viewport) so that only 90% of the original shows after the first iteration, then on the second iteration, to appear a consistent speed, 90% of the screen from the result of the first iteration needs to be showing.
So instead of decreasing the original viewport linearly 90%, 80%, 70% etc. You need to decrease it exponentially. 90%, 81%, 72.9% etc...
After a bit of thinking, I finally came up with a formula.
X(i)=a*(p^i)
X(i) is the point after i iterations. a is the original position of the point. p is a scaler.
So the idea is, in order to move from a to b in n number of iterations, you need to calculate the value of p in such a way that when i=1 then X(i) = a and when i=n then X(i) = b.
Here's the nifty little formula for that.
p = (b/a)^(1/n)
There's only a few restrictions. One is that the center of the rectangle you are zooming into must be translated to the center of the world before the calculations are performed, which means everything must be translated back afterwards.
Another is that b cannot be 0. That can only happen if the rectangle you are zooming into has either 0 width or 0 height.

Here is an example of zooming with the above method. I have drawn a bunch of polygons around the screen to give a little better perspective of what's going on. On part of the screen is a bunch of concentric circles which marks the bullseye of where I want to zoom into.
As you can see, the zooming appears very consistent.
SuperStrict
Graphics 800,600

Type TEntity 'for making shapes on the screen
	Field numsides:Int
	Field x:Double
	Field y:Double
	Field radius:Double
End Type
Global List:TList = CreateList()

For Local i:Int = 1 To 100 'create a bunch of randomly placed, randomly shaped objects
	Local Entity:TEntity = New TEntity
	Entity.x = Rnd(800)-400
	entity.y = Rnd(600)-300
	Entity.radius = Rnd(.1,50)
	entity.numsides = Rand(3,8)
	List.Addlast(Entity)
Next
For Local i:Int = 1 To 20 'create a "Bullseye" shape for where we're going to zoom to
	Local Entity:TEntity = New TEntity
	Entity.x = 70
	Entity.y = 65
	Entity.numsides = 20
	Entity.Radius = i
	List.AddLast(Entity)
Next
SetOrigin 400,300 'Make 0,0 at the center of the screen

While Not KeyHit(KEY_ESCAPE) And Not AppTerminate()
	Cls
	Drawobjects(-400,-300,400,300)
	SetColor 255,255,0
	DrawText "Press Z to zoom",10,10
	SetColor 255,255,255
	Flip
	If KeyHit(KEY_Z) Then zoom(50,50,90,80,600)
Wend
 
Function DrawObjects(x1:Double,y1:Double,x2:Double,y2:Double) 'Goes through the list and draws onto the World coordinates
	For Local Entity:TEntity = EachIn List
		Local Angle:Double = 360.0/Double(Entity.numsides)
		Local ox:Double = Entity.Radius + Entity.X
		Local oy:Double = Entity.y
		While Angle < 361
			Local x:Int = Cos(Angle)*Entity.Radius+Entity.x
			Local y:Int = Sin(Angle)*Entity.Radius+Entity.y
			Line(ox,oy,x,y,x1,y1,x2,y2)
			Angle :+ 360.0/Double(Entity.numsides)
			ox=x
			oy=y
		Wend
	Next	
			
End Function

Function Line(sx:Double,sy:Double,ex:Double,ey:Double,x1:Double,y1:Double,x2:Double,y2:Double)' translate from world coordinates to screen and renders
	Local cx:Double = (x2-x1)/Double(2)+x1
	Local cy:Double = (y2-y1)/Double(2)+y1
	Local sxs:Int = (sx-cx)/(x2-x1)*800
	Local sys:Int = (sy-cy)/(y2-y1)*600
	Local exs:Int = (ex-cx)/(x2-x1)*800
	Local eys:Int = (ey-cy)/(y2-y1)*600
	
	DrawLine sxs,sys,exs,eys
End Function

'zx1, zy1, zx2, zy2 is the rectangle we are zooming into
Function Zoom(zx1:Double,zy1:Double,zx2:Double,zy2:Double,frames:Int)
	Local cx:Double = (zx2-zx1)/2.0 'the distance from the edge to the center of the zooming rect
	Local cy:Double = (zy2-zy1)/2.0
	Local tx:Double = cx+zx1 'distance from the center of the zooming rect to the world origin
	Local ty:Double = cy+zy1
	
	Local px:Double = (cx/400.0)^(Double(1)/Double(frames)) 'calculate the scalar for zooming
	Local py:Double = (cy/300.0)^(Double(1)/Double(frames))
	Local ptx:Double = (cx/(400.0+tx))^(Double(1)/Double(frames))'calculate the scaler for translating
	Local pty:Double = (cy/(300.0+tx))^(Double(1)/Double(frames))
	
	For Local i:Int = 1 To frames
		Local nx1:Double = Double(-400)*(px^Double(i)) 'corner relative to rect center
		Local nx2:Double = -nx1 'opposite corner are negative of each other
		Local ny1:Double = Double(-300)*(py^Double(i))
		Local ny2:Double = -ny1
		
		Local ntx:Double = -((400.0+tx)*(ptx^Double(i))-tx) 'Where the corner of the screen is after translating
		Local nty:Double = -((300.0+ty)*(pty^Double(i))-ty)
		Local nsx:Double = ntx-nx1 'The amount needed to translate the current viewport from the zooming rect to the world coordinates
		Local nsy:Double = nty-ny1
		Cls
		drawobjects(nx1+nsx,ny1+nsy,nx2+nsx,ny2+nsy) 'draw objects at current viewport dimentions
		Flip
	Next
	WaitKey()	
End Function