Bitmap Font Rotation Problem

BlitzMax Forums/BlitzMax Programming/Bitmap Font Rotation Problem

VomitOnLino(Posted 2010) [#1]
Hey Folks,

Basically I have a problem rotating bitmap fonts, made out of individual sprites/slices, around a given pivot point. Right now when I rotate every letter rotates on it's own accord, as instead rotating the word around it's center.

	Method Draw(text:String, x:Double, y:Double, Center:Byte = False, RightJustify:Byte = False, RoundX% = False, RoundY% = False, CentreVertically% = 0)
		'By default the text is draw Left-Justified, but you can alter that with the Center and RightJustify params.
		'Sometimes drawing characters at sub-pixel positions makes them look blurry, so
		'if you want them to look crisp use the RoundX and RoundY params.  However if you
		'plan to move the text around on-screen smoothly, then you should not use those param.

		'Ingore null strings.
		If text="" Then Return

		Local char:Int
		Local scale_x:Float
		Local scale_y:Float
		
		HandleX=0
		
		'Store the current scale.
		GetScale(scale_x,scale_y)
				
		'Justify.
		If Center Then
			Local halfwidth:Double = (StringWidth(text) / 2)
			If ChangeHandle Then
				HandleX:+halfwidth
			Else
				x:-halfwidth*scale_x
			EndIf
		EndIf
		If RightJustify Then
			If ChangeHandle Then
				HandleX:+StringWidth(text)*scale_x			
			Else
				x:-StringWidth(text)*scale_x
			EndIf
		EndIf

		'Centre Vertically?
		If CentreVertically Then
			Local halfheight:Double = (StringHeight(TestString) / 2)
			If ChangeHandle Then
				HandleY:+halfheight
			Else
				y:-halfheight * scale_y
			EndIf		
		EndIf

	 	'Scale the font by the ScaleFactor.
		scale_x :* ScaleFactor
	 	scale_y :* ScaleFactor
		
		'Loop and output each character.
		Local a:Byte
		For a = 0 To Len(text) -1
			Local letter$ = Mid(text,a+1,1)
			char = Asc(letter)
			
			
			
			If char >= MAX_BITMAPFONT_STANDARD_CHARS Then char=FindChar(letter) 
			
			If char >= 0 Then
				Local xpos:Double = xPos[char]
				Local ypos:Double = yPos[char]
				Local width:Double = width[char]
				Local height:Double = height[char]
				
				
				'Take into account any character offset.
				Local x1:Double = x
				Local y1:Double = y 
				If Not ChangeHandle Then
					x1 :+ xOffset[char] * scale_x
					y1 :+ yOffset[char] * scale_y
				EndIf
				Local w:Double = width * scalefactor
				Local h:Double = height * scalefactor
				'Local w:Double = width * TextureW * scalefactor 'For pre BMax 1.36
				'Local h:Double = height * TextureH * scalefactor 'For pre BMax 1.36

				'Shall we round the coords for a nice crisp image?
				'Only round if the text is not scaled, otherwise the letters can sometimes draw at different Y coords and it looks bad.
				If RoundX And scale_x=1 Then
					x1 = ccRound(x1)
					w = ccRound(w)
				EndIf
				If RoundY And scale_y=1 Then
					y1 = ccRound(y1)
					h = ccRound(h)
				EndIf
			
				'Prepare to move onto next character.
				Local increment: Double = (xAdvance[char] + CharGap)

				'Deal with the Handle.
				If ChangeHandle Then
					DrawSubImageRect(Image, x1-(HandleX-xOffset[char]*scalefactor), y1-(HandleY-yOffset[char]*scalefactor), w, h, xpos, ypos, width, height) 
					x1:+20*a
					DrawSubImageRect(Image, x1, y1, w, h, xpos, ypos, width, height) 
					'Change the handle not the x coord.
					HandleX :- increment*scalefactor
					
				Else
					'Draw normally.
					DrawSubImageRect(Image, x1, y1, w, h, xpos, ypos, width, height) 
					'DrawImageRect(Image,x1,y1,w,h) 'For pre BMax 1.36
					x :+ increment*scale_x
				EndIf
			EndIf
		Next
		
	End Method


I sadly can't post the full code as it's part of GreyAlien's Framework, which I paid for and I'm sure it would upset him.

Also I probably suck at trigonometry because I had several shots at it but failed to achieve the wanted effect.

Thanks!


Jesse(Posted 2010) [#2]
http://www.blitzmax.com/Community/posts.php?topic=91739


VomitOnLino(Posted 2010) [#3]
Thank you, I tinkered around with the code a bit, but it still seems to have the same problem (letters rotate individually as well, while rotating on the "global" axis).

Also the code seems a bit sluggish, when implemented, probably because it's querying graphics a lot during runtime.

I guess both of those could be linked to me not being fully apt with max, yet.. ;)


Jesse(Posted 2010) [#4]
you are not supposed to query the graphics constantly and won't need to. You are doing something wrong and not implementing it correctly. this is as fast as it's going to get for anything you do that has to do with rotation and scale and "is" extremely fast. It's your implementation that is causing the problems. but I can't tell what you are doing wrong unless you post some running code.


VomitOnLino(Posted 2010) [#5]

SuperStrict
Graphics 800,600

Global angle:Int

Repeat 
	Cls
	
	SetRotation angle
	
	For Local i:Int=1 To 200 Step 10
		DrawRect (300+i,300,8,8)
	Next
	
	angle :+ 1.0
	
	Flip()
	
Until KeyDown(key_escape)


This is basically my problem in condensed form. I want those squares to move synchronously around a common pivot point which should be in their middle.


DrDeath(Posted 2010) [#6]
Of course this won't work as you want: SetRotation only sets the rotation of the individual shapes you paint, it does not rotate the entire screen. For the effect you want, you'll have to transform the coordinates you paint your squares at according to the angle by yourself.


VomitOnLino(Posted 2010) [#7]
Well maybe I communicated this poorly -- or guess I am misunderstanding you, but I don't want to rotate the screen. Just the squares - in synchronization.


TomToad(Posted 2010) [#8]
You are misunderstanding DrDeath. What he means is that when you rotate an object around a set point, the object's x,y coordinates will change also.
  h <- 90 Degrees
  e
hello <- 0 Degrees
  l
  o

The 'h', 'e', second 'l', and 'o' now are at different x,y coordinates at 90 degrees than at 0 degrees. So you must translate (move to) the letters to the correct position.
The formula you want to do so is
x = Cos(Angle) * Distance_To_Rotation_Point
y = Sin(Angle) * Distance_To_Rotation_Point

Here is a small example. I have set up a type for each "Letter", but you could recalculate the distance "on the fly" each time if it is likely the word will change.



Jesse(Posted 2010) [#9]
is this what you mean?:
SuperStrict
Graphics 800,600
paste
Global gfx:TMax2dGraphics = tmax2dgraphics.Current()
Global scale:Float = 4.0
Global angle:Float = 0.0
SetScale scale,scale

Repeat 
	Cls
	
	SetRotation angle
	Local x:Float = 300
	Local y:Float = 300
	For Local i:Int=1 To 200 Step 10
		x :+ 10 * gfx.tform_ix 
		y :+ 10 * gfx.tform_jx
		DrawRect (x,y,8,8)
	Next
	
	angle :+ 1.0
	
	Flip()
	
Until KeyDown(key_escape)



VomitOnLino(Posted 2010) [#10]
That's it. You made me look dumb, but I'm still happy for it. :)
It's similar to what you linked before, but I guess Ifailed to understand it's merit.

Thanks a million!

EDIT: Didn't see TomToads example. This also works great thanks to you both!