It is currently April 19th, 2024, 11:51 pm

Working with Angle on a String Meter (redux)

Our most popular Tips and Tricks from the Rainmeter Team and others
User avatar
jsmorley
Developer
Posts: 22629
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Working with Angle on a String Meter (redux)

Post by jsmorley »

The Angle option on a String meter can be used to rotate the text in a string meter to any angle, either positive or negative.

Here we have rotated the string "Hello World" -45 degrees:
Angle01.png
The Angle option is pretty simple in principle. You simply add the option to the String meter, defining the number (negative or positive) of radians you want to rotate the string by. It is simpler to understand if you think about this in degrees instead of radians, with the original horizontal orientation being 0 degrees. Then just use the Rad(degrees) mathematical function to convert the degrees to radians for the meter.

Angle=(Rad(-45))

The text will be rotated the defined number of radians inside the meter, using the X and Y of the meter (the top-left corner in the case of the default StringAlign=Left) as the anchor point of the rotation.

However, there are some very important rules to understand in order to successfully use the Angle option.
What we will do is lay out these rules, and demonstrate how you can work within them to get the results you want.

First, let me acknowledge right up front that the Angle option is just poorly designed in Rainmeter. Mostly this is due to the fact that the rotation does not change the resulting size of the meter, (like ImageRotate can...) and this makes it a tad horrible to use. Issues with backwards compatibility make that difficult to fix, and eventually there may have to be something like a brand new MeterRotate option for this kind of thing that works on any meter type. (more like a really simple TransformationMatrix) In the meantime however, it does work and can be used to create some nice string effects.

Rule One : The String meter is not rotated. The text inside is the meter is.
It is important to understand this as the first and primary thing. When you create a String meter, the meter that is created is a rectangle. It has a horizontal orientation, and will be sized either automatically to fit the string using the defined font, or manually by setting W and H on the meter.

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1

[MeterHoriz]
Meter=String
X=0
Y=0
FontSize=30
FontColor=47,47,47,255
SolidColor=150,150,150,255
AntiAlias=1
Text=Hello World
Angle02.png
This creates a meter that is a horizontal rectangle of 205 X 44 pixels, based on the FontSize and the Text.

Rule Two : The size and orientation of the meter is not changed by the Angle option.
Again, it is important to remember the the "text" is rotated inside the "meter". The meter itself is not rotated, its size is not changed, and in every sense, the meter itself has no idea that any angle was even applied.

Rule Three : The rotation of the text inside the meter will almost certainly make it so the text no longer fits inside the meter.
To demonstrate, let's just add a second meter at the same spot, with the same text as our first one. Then we will rotate it -45 degrees:

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1

[MeterHoriz]
Meter=String
X=0
Y=0
FontSize=30
FontColor=47,47,47,255
SolidColor=150,150,150,255
AntiAlias=1
Text=Hello World

[MeterAngle]
Meter=String
X=0
Y=0
FontSize=30
FontColor=255,255,255,255
AntiAlias=1
Angle=(Rad(-45))
Text=Hello World
Angle03.png
Note that you can see the bottom left part of the text from our second angled meter, but the entire rest of the text has been "rotated out" of the boundaries of the meter. That second meter is still a 205 X 44 pixel rectangle, and our "Hello World" string no longer even begins to fit in it. That light grey box behind our first meter in fact also defines the size and position of our second meter, in spite of the Angle we applied to it.

Rule Four : The size of the actual skin will also not change to reflect the new angle of the text.
Remember that the size of the String meter is never changed by the Angle option. That means the size of the skin, which is automatically defined by the overall size and position of all meters in the skin, is also not changed. Unless the String meter is part of a larger skin that has a background or other meters that cause the overall size of the skin to be large enough, our rotated text is almost certain to not only "rotate out" of the meter, (which is sorta OK, the text will still be drawn outside the meter as long as it is still inside the skin) but actually "rotate out" of the entire skin and be truncated. (which is really not OK) That should be clear from our image above.

DynamicWindowSize in the [Rainmeter] section is no help with this. Remember, nothing has changed "size" when we use the Angle option on a String meter.

Rule Five : The X and Y of the String meter play a role in making the skin fit the rotated text.
What this means is that while creating a background meter so that our skin is big enough to hold the rotated text is the right thing to do, it should be remembered that you may also need to "move" the String meter so when it is rotated it is entirely contained by the background meter. The changes you need to make to X and Y will vary wildly, depending on the angle of rotation, and whether it is positive (swinging down around the anchor) or negative (swinging up around the anchor).

Let me demonstrate:

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1

[MeterBack]
Meter=Image
W=172
H=187
SolidColor=47,47,47,255

[MeterHoriz]
Meter=String
X=0
Y=0
FontSize=30
FontColor=47,47,47,255
SolidColor=150,150,150,255
AntiAlias=1
Text=Hello World

[MeterAngle]
Meter=String
X=0
Y=0
FontSize=30
FontColor=255,255,255,255
AntiAlias=1
Angle=(Rad(-45))
Text=Hello World
Angle04.png
Here I created a background Image meter that will be behind the String meters, and is big enough to hold our eventual rotated text. (How I determined that size I will get to in a second). However, since the String meter is defined at X=0 and Y=0, it doesn't really help does it? The text still "rotates out" of the overall skin at the top.

What we need to do is use X and Y to "move" the String meter to a position where, when the text is rotated, it is entirely contained by the background meter.

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1

[MeterBack]
Meter=Image
W=172
H=187
SolidColor=47,47,47,255

[MeterHoriz]
Meter=String
X=0
Y=141
FontSize=30
FontColor=47,47,47,255
SolidColor=150,150,150,255
AntiAlias=1
Text=Hello World

[MeterAngle]
Meter=String
X=0
Y=141
FontSize=30
FontColor=255,255,255,255
AntiAlias=1
Angle=(Rad(-45))
Text=Hello World
Angle05.png
Notice that I changed both String meters to use Y=141 instead of Y=0. That moves them down to the bottom of our Image meter background, and when the text is rotated, it fits in the background meter and isn't truncated by the skin. As I said above, Rainmeter will still draw the text even if it is outside the boundaries of the meter, as long as it isn't also outside the boundaries of the overall skin.

Rule Six : The anchor / center of rotation is based on the "font", not the "string".
By that I mean that most fonts have some "slack space" around the actual character glyphs in the font file. When the string is rotated, that slack space is rotated with it. This will be much more pronounced if you don't set AccurateText=1 in the [Rainmeter] section of the skin, but even if you do it will play a small role in the final effect.

So to have my rotated string properly rotate exactly around the top left corner of the "visible text", to match with the first non-rotated String meter, I would make a couple of tweaks to the X and Y of the rotated meter.

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1

[MeterBack]
Meter=Image
W=172
H=187
SolidColor=47,47,47,255

[MeterHoriz]
Meter=String
X=0
Y=141
FontSize=30
FontColor=47,47,47,255
SolidColor=150,150,150,255
AntiAlias=1
Text=Hello World

[MeterAngle]
Meter=String
X=-4
Y=144
FontSize=30
FontColor=255,255,255,255
AntiAlias=1
Angle=(Rad(-45))
Text=Hello World
Angle06.png
I changed the X to -4 and the Y to 144, to move the meter a tad "left" and "down" to remove the impact of the slack space in the actual font.

Rule Seven : Most of this sizing and positioning is going to be trial-and-error. Sorry...
There really is just no way that you are going to be able to automate or even calculate what the size of any background meter, and the X and Y of the String meter will need to be to make this work right for you.

Mostly this is due to the fact that in spite of whatever clever formulas you might come up with, the variable width of characters in most fonts, the varying metrics of different fonts, and the fact that there is no reliable formula for "number of characters X point size = number of pixels" is just going to make it something you have to tweak.

What I recommend is that you create your meter non-rotated first, get a sense of how big it is, and then create a visible background Image meter that will be more than large enough to hold it when rotated. Change the X and Y To move the meter where it will fit best in the background meter, and then rotate it with the Angle option. Then you can play with the W and H of the background Image meter, and the X and Y of the String meter, until you get things positioned correctly, with the "text" fitting as efficiently as possible in the background.

Hint: You can find the size of a meter by simply adding something like:

MouseOverAction=[!Log "#CURRENTSECTION# is [#CURRENTSECTION#:W] X [#CURRENTSECTION#:H]"]

to the meter. Then just mouse over it to see the size in the About / Log dialog. Remove this when you are done testing.

Then you can simply set the background image meter to SolidColor=0,0,0,0 (invisible), and you should end up with just your rotated string working perfectly. Do not remove the background meter in my example. It isn't there just to help you position things, but will be required to ensure the overall skin is big enough, even if it is "invisible".

Code: Select all

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1

[MeterBack]
Meter=Image
W=172
H=187
SolidColor=0,0,0,0

[MeterAngle]
Meter=String
X=-4
Y=144
FontSize=30
FontColor=255,255,255,255
AntiAlias=1
Angle=(Rad(-45))
Text=Hello World
Angle01.png
Final notes

Remember, our actual String meter has not changed size or shape.

Any mouse actions you put on that string meter will react, and only react, to the rectangle defined by the original, non-rotated meter. In our example above, a LeftMouseUpAction on the String meter will be completely ignored by about 90% of the "visible" string. There is no perfect solution, but one thing you can do is to put any mouse actions on that background Image meter instead, and set SolidColor=0,0,0,1. At least then the full "visible" string reacts to the mouse. That is one reason why it is worth the effort to make the "text" fit the "background" as exactly as possible. The background image will still be a rectangle, so you may have a lot of empty space that reacts to the mouse, but that is probably better in the long haul than having most of the text not react.

You can improve on this situation quite a bit by creating really any image in PhotoShop or Gimp the same size as the original String meter. (205 X 44 in our example). Then you can use ImageAlpha=1 and ImageRotate=-45 on an Image meter using that image, to have it match the rotation of the string. Putting the mouse actions on this Image meter will help, since the mouse will ignore any "completely invisible" portions of the meter, and you will get a result that is a lot more satisfying.

Relative positioning with "r" and "R" is just not going to work right. The relative position of the next meter you define with X=5R or any other relative position option is going to be based on the size and shape of the original non-rotated previous meter, and you are just not going to get any useful behavior.

The StringAlign option can certainly be used, but it should be remembered that StringAlign is also based on the X and Y and W and H of the meter. Since StringAlign=Center means that the string will be centered on X and then equally divided to the left and right of X, this can cause its own issues with the size of the meter and / or the size of the skin. Adding an Angle to the mix can be managed, but with a center of rotation that isn't top left anymore, it might take some additional tweaking of the W/H/X/Y values to get things right.
You do not have the required permissions to view the files attached to this post.
User avatar
jsmorley
Developer
Posts: 22629
Joined: April 19th, 2009, 11:02 pm
Location: Fort Hunt, Virginia, USA

Re: Working with Angle on a String Meter (redux)

Post by jsmorley »

Just to demonstrate the effect of changing the center of rotation based on X and Y with StringAlign:

Code: Select all

[Rainmeter]
Update=100
DynamicWindowSize=1
AccurateText=1

[MeasureRotate]
Measure=Calc
Formula=(MeasureRotate % 360) + 10

[MeterBack]
Meter=Image
W=100
H=100
SolidColor=47,47,47,255

[MeterTwo]
Meter=String
X=50
Y=50
StringAlign=CenterCenter
FontSize=12
FontColor=255,255,255,255
SolidColor=150,150,150,255
AntiAlias=1
Text=Hello World
Angle=(Rad([MeasureRotate]))
DynamicVariables=1
test.gif
As you can see, this will make positioning a bit tricky, as while X works fine, the center of rotation is also based on where Y (the top) is defined for the meter, and there is really no way to have the anchor be actually in the vertical "middle" of the text string.

StringAlign doesn't set the Y to the vertical "middle" of the string, but rather to a point where setting the "top" of the meter will cause the meter to be centered on what you define for Y. The actual Y of the meter will be this new "top" number, which will be what you defined as the center, less half the height of the meter. (in this case Y=42) With Angle seeing 42 instead of 50 for the vertical center, you get that off-kilter rotation. Or as I like to call it, "a steaming bucket of fail".

While this effect as is might have its uses, getting a real "rotate around the center" would really require TransformationMatrix, or some reasonably complicated dynamic values for Y based on the percentage of rotation.
You do not have the required permissions to view the files attached to this post.
User avatar
ryanchuang
Posts: 30
Joined: September 13th, 2019, 4:23 am

Re: Working with Angle on a String Meter (redux)

Post by ryanchuang »

Hi,it is possible that rotate each letter/character in vertical way,not the whole phrases?? With this,as Chinese/Korean/Japanese characters are being written in that way.
User avatar
balala
Rainmeter Sage
Posts: 16147
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: Working with Angle on a String Meter (redux)

Post by balala »

ryanchuang wrote: September 13th, 2019, 4:31 am Hi,it is possible that rotate each letter/character in vertical way,not the whole phrases?? With this,as Chinese/Korean/Japanese characters are being written in that way.
No, unfortunately it's not. When applying an Angle option to a String meter, it applies to the whole string. In fact I don't know any way to rotate just one single letter.
Sorry...
User avatar
ryanchuang
Posts: 30
Joined: September 13th, 2019, 4:23 am

Re: Working with Angle on a String Meter (redux)

Post by ryanchuang »

Oh,thank you. I did find a way to accomplish that only because the strings wasn't from outside of source,ex streaming weather condition from weather.com. I was seeking some ways/shapes/forms that can rotate each letter from streaming source which usually has long string of texts.
User avatar
balala
Rainmeter Sage
Posts: 16147
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: Working with Angle on a String Meter (redux)

Post by balala »

ryanchuang wrote: September 14th, 2019, 3:50 am I did find a way to accomplish that only because the strings wasn't from outside of source,ex streaming weather condition from weather.com.
Did you? How? I'd be curious.
User avatar
ryanchuang
Posts: 30
Joined: September 13th, 2019, 4:23 am

Re: Working with Angle on a String Meter (redux)

Post by ryanchuang »

Well,it's very simple,just add #CRLF# between the letter/character which would make string text vertical. But,of course,you have to fine-tuned the line to make the entire string align adjustily. I haven't really done that because I knew from the get-go,the streaming weather condition from Weather.com isn't going to work which would make the entire "theme" unreadable.
User avatar
balala
Rainmeter Sage
Posts: 16147
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: Working with Angle on a String Meter (redux)

Post by balala »

ryanchuang wrote: September 14th, 2019, 7:35 am Well,it's very simple,just add #CRLF# between the letter/character which would make string text vertical. But,of course,you have to fine-tuned the line to make the entire string align adjustily. I haven't really done that because I knew from the get-go,the streaming weather condition from Weather.com isn't going to work which would make the entire "theme" unreadable.
Yes, this way it might work, however for this you have to modify the string which has to be shown. Not too conveniable...
User avatar
ryanchuang
Posts: 30
Joined: September 13th, 2019, 4:23 am

Re: Working with Angle on a String Meter (redux)

Post by ryanchuang »

Oh,well. It works,it doesn't matter which string is from. It does go vertical all the way which is still a problem considering the string meters has to be on the second column,third column,fourth column,etc., when the "theme" I am working on has 9 rows,so it has to be has 9 columns as well after being vertically aligned. Still do not know how though,any idea?? :confused:
User avatar
balala
Rainmeter Sage
Posts: 16147
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: Working with Angle on a String Meter (redux)

Post by balala »

ryanchuang wrote: September 14th, 2019, 10:41 am It works,it doesn't matter which string is from.
Yes, but how do you add those #CRLF# variables between each two consecutive characters if it doesn't matter? Post please a sample code.