April 24, 2024, 09:56:21 AM

News:

IonicWind Snippit Manager 2.xx Released!  Install it on a memory stick and take it with you!  With or without IWBasic!


Rotated Text

Started by GWS, August 26, 2011, 03:01:55 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

GWS

August 26, 2011, 03:01:55 PM Last Edit: August 27, 2011, 01:01:19 AM by GWS
Hi folks,

Here's an experiment I've been playing with to rotate text  :)

I know there is a method using Window's API routines, but I've not been able do that so far.

So here I'm using just standard Basic commands to do the job.

The method uses an off-screen working window in which the un-rotated text is written.

This window is then 'scanned' for pixels of a given text colour, and these are then carried across to produce the rotated text at the required co-ordinates in the destination window.

When rotating text, it is easy to get a few 'missing' pixels, giving a slightly ragged look.

The method used here is pretty good - and particularly good at zero and 90 degrees.  So it would be a good solution for annotating graph axes.


' Test program to demonstrate text rotation
' GWS - Aug 2011

def w,w2:window
def i,j,varW,varH,pencol:int
def srcX,srcY,run,tx,ty:int
def twopi,d2r,angle:double
def a$,p:string

autodefine = "off"

declare slopetext(srcwin:window,theta:double,cX:int,cY:int,pencol:int)

twopi = 8 * atan(1)
d2r = twopi/360

' open a window ..
window w,-600,0,600,400,0,0,"Creative Basic",messages
setwindowcolor w,rgb(0,0,30)
centerwindow w

control w,"B,Exit,(600-70)/2,310,70,30,0,1"

a$ = "Rotated" :' text to be rotated ..

setfont w,"Arial",20,600 :' required font ..
gettextsize w, a$, varW, varH :' find the text width (varW) to be scanned ..

' open a working window off-screen to write the text to temporarily ..
' this is sized with height and width both set to the text width varW (to allow for rotation)

window w2,-varW,0,varW,varW,@nocaption,w,"",messages2
setwindowcolor w2,rgb(0,30,130)

setfont w2,"Arial",20,600 :' use the same font in the working window ..
frontpen w2,0xffffff :' set the text colour to be tracked ..
tx = 1 :' set the text x co-ord in the working window
ty = (varW-varH)/2 :' set the text y co-ord half way down the working window

move w2,tx,ty :' place the text in the working window ..
print w2,a$

angle = 60 :' angle to rotate the text 

' call the rotation subroutine ..
' w2 - the working window
' angle - the angle in degrees to rotate the text (Positive - anti-clockwise, Negative  - clockwise)
' cX - the centre x co-ord required for the rotated text ..
' cY - the centre y co-ord required for the rotated text ..
' pencolour - the required text colour

slopetext(w2,angle,300,200,0x00ffff)

closewindow w2

run = 1

WAITUNTIL run = 0
stoptimer w
CLOSEWINDOW w
END

SUB messages
select @class
case @idclosewindow
run = 0
case @idcontrol
if @controlID = 1 then run = 0
endselect
RETURN

sub slopetext(srcwin,theta,cX,cY,pencol)
def centrX,centrY:int

' routine for sloping text at angle theta ..
' cX is the centre x co-ord for the rotated text
' cY is the centre y co-ord for the rotated text

centrX = varW/2 :' centre x co-ord of original text
centrY = varW/2 :' centre y co-ord of original text
theta = d2r*theta :' convert to radians

for i = 1 to varW
for j = 1 to varW
srcX = (i-centrX)*cos(theta) - (j-centrY)*sin(theta) + centrX
srcY = (i-centrX)*sin(theta) + (j-centrY)*cos(theta) + centrY

p = hex$(getpixel(w2,srcX,srcY))

if p = hex$(0xffffff)
' (this positioning is only ad-hoc - it needs improving)
pset w,cX+i-varW/2,cY+j-varW/2-varH,pencol
endif
next j
next i

return

SUB messages2
' there are no messages used for this working window ..
RETURN


It takes a few milliseconds to generate the rotated text, but I find it acceptable.

I've included the .exe for the program so that folk who haven't loaded Creative can try it out ..

best wishes, :)

Graham





Tomorrow may be too late ..

GWS

It occurred to me that some folk might not be too familiar with defining colours using hexadecimal values.

Most common format in Basic is to use the RGB(red,grn,blu) function.

Here, the red, grn , blu values are integers in the range 0 to 255.  A value of zero means none of that colour is contained in the composite colour.  A value of 255 means that the strongest amount of that colour is included.

Hex format is useful when passing colour values to a subroutine or graphics function.

The Hex format is:  0xBBGGRR (note the reversed b,g,r colour sequence).

Each hex digit is 0 to F, and taking the two hex digits together for each colour content, provides ranges from 00 to FF - that is 0 to 255 in decimal.

So a pure red colour is specified as 0x0000FF.  Pure Yellow is 0x00FFFF.   Black is 0x000000 (or just 0).

If all the hex quantities have the same value (eg 0x444444) you get a tone of grey, ranging from 0x000000 (Black) to 0xFFFFFF (White).

Once you get used to it, it provides a useful and concise method of specifying colours.

all the best, :)

Graham
Tomorrow may be too late ..

GWS

Hi,

I've improved the code slightly.


' Test program to demonstrate text rotation
' GWS - Aug 2011
' (slightly improved for speed and positioning)

def w,w2:window
def i,j,varW,varH:int
def fontsize,fontweight,pencol:int
def srcX,srcY,run,wW,wH:int
def twopi,d2r,angle:double
def a$,p:string

autodefine = "off"

declare slopetext(srcwin:window,theta:double,cX:int,cY:int,pencol:int)

twopi = 8 * atan(1)
d2r = twopi/360

wW = 600: wH = 400
window w,-wW,0,wW,wH,0,0,"Text Rotation",messages
setwindowcolor w,rgb(0,0,30)
centerwindow w

control w,"B,Exit,(600-70)/2,310,70,30,0,1"

a$ = "Rotated" :' text to be rotated ..
fontsize = 24 :' required font size
fontweight = 600 :' required font weight

setfont w,"Arial",fontsize,fontweight :' required font size and weight ..
gettextsize w, a$, varW, varH :' find the text width (varW) to be scanned ..

' open a working window to write the text to temporarily ..
' Make the height and width both equal to the text width varW (to allow for rotation)
window w2,0,0,varW,varW,@nocaption,w,"",messages2

' (Surprisingly, a working window of zero size works as well)
' Try using this and comment out the one above ..
'window w2,0,0,0,0,@nocaption,w,"",messages2

setfont w2,"Arial",fontsize,fontweight :' required font size (same as specified in main window) ..
setwindowcolor w2,0x555500 :' any colour other than the tracking colour White
frontpen w2,0xffffff :' set the text colour to be tracked ..

move w2,0,(varW-VarH)/2 :' place the text at the centre of the working window ..
print w2,a$

angle = 45 :' angle to rotate the text 

' call the rotation subroutine ..
' w2 - the working window
' angle - the angle in degrees to rotate the text (Positive - anti-clockwise, Negative  - clockwise)
' cX - the centre x co-ord required for the rotated text ..
' cY - the centre y co-ord required for the rotated text ..
' pencolour - the required text colour

slopetext(w2,angle,300,150,0x00ffff)

run = 1

WAITUNTIL run = 0
stoptimer w
CLOSEWINDOW w
END

SUB messages
select @class
case @idclosewindow
run = 0
case @idcontrol
if @controlID = 1 then run = 0
endselect
RETURN

sub slopetext(srcwin,theta,cX,cY,pencol)
def centrX,centrY,x,y:int
def c,s:double

centrX = varW/2 :' centre x co-ord of original text in the working window
centrY = varW/2 :' centre y co-ord of original text in the working window
theta = d2r * theta :' convert angle to radians
' do some constant calculations outside the loops to improve speed a bit ..
c = cos(theta)
s = sin(theta)
x = cX - varW/2
y = cY - varW/2

' loop through pixels in the destination square ..
for i = 1 to varW
for j = 1 to varW
srcX = (i - centrX) * c - (j - centrY) * s + centrX
srcY = (i - centrX) * s + (j - centrY) * c + centrY

' check if the translated pixel in the working window is the tracking colour ..
p = hex$(getpixel(w2,srcX,srcY))
if p = hex$(0xffffff)
' if so, set the rotated pixel in the destination square ..
pset w,x+i,y+j,pencol
endif
next j
next i

return

SUB messages2
' there are no messages used for this working window ..
RETURN



This is a fierce problem, and I think I give up ..  ::)

Imagine a block of text W pixels wide and H pixels high for a given font.

At 0 degrees rotation, the 'i' loop ideally scans cX-W/2 to cX+W/2, and the 'j' loop scans cY-H/2 to cY+H/2.  (cX,cY being the co-ords of the centre of the destination square).

At 90 degrees, 'i' should run from cX-H/2 to cX+H/2, and 'j' from cY-W/2 to cY+W/2.

At 45 degrees, the mind boggles - the ideal 'i' and 'j' start and end values are functions of each other. :o

The simplest, and slowest, method is to let both 'i' and 'j' run through the whole range of values in the square of size W, that is from 1 to W.

So we are sweeping through all the pixels in the square rather than the smaller number required to find the text pixels, if only we knew where to start and end the 'i' scan on each scan line 'j'.

When W >> H, there would be a good saving in time, but the calculations involved might reduce that saving quite a bit.  As I said - it's a horrible problem.

I suppose the old adage applies - 'there's got to be a better way'  ::)

best wishes, :)

Graham



Tomorrow may be too late ..