It is currently June 17th, 2019, 3:44 am

[Lots of Math] Moon Icon Rotation on a Sky Dial

Help with creating, editing & fixing problems with skins
User avatar
raiguard
Posts: 630
Joined: June 25th, 2015, 7:02 pm
Location: The Sky, USA

[Lots of Math] Moon Icon Rotation on a Sky Dial

raiguard » December 30th, 2018, 3:22 am

EDIT: I have created a very long and very detailed explanation of exactly what is going on. If you wish to dive in and try to help me solve this, click here. Thanks!

Hello everyone. I am attempting to create a sky dial using shape meters, and while I have done a basic one before, the one I am currently creating is very complicated and convoluted. I am creating this thread because I am bound to have many math-related issues with path shapes.

Here is my first problem:

For the moon icon, I would like to be able to display the current moon phase, as well as the angle of the illuminated side as you would see it in the sky. I have done both of these things before, that is not my problem. My problem arises when I attempt to implement them on the sky dial:
atrodial PROBLEMS.gif
That's not right at all!

My only hypothesis so far is that when the moon limb shape (the white illuminated part) is at certain angles, it's changing where the top-left of the limb's bounding box is, and therefore, the arc rotation anchor point moves as well. However, I have absolutely no clue how to fix it. I need to somehow create a function of the moon's limb rotation that will move the arc rotation anchor back to where it is supposed to be (signified by the green dot).

Does anybody have any ideas? Much appreciated. :D

AstroDial.ini:

Code: Select all

[Rainmeter]
MiddleMouseUpAction=[!Refresh]
AccurateText=1

[Variables]
; Colors
colorBg=15,15,15
colorDialArc=150,150,150
colorMoon=200,200,200
colorMoonBg=30,30,30
colorMoonBorder=70,70,70
colorSun=250,222,110
; Dial settings
dialRadius=250
dialStartX=3
dialArcThickness=6
dialObjectRadius=23
; Moon settings
moonBorderThickness=3
moonInnerRadius=(#dialObjectRadius# - #moonBorderThickness#)
moonArcStartX=(#dialStartX# - #dialObjectRadius#)
moonArcStartY=(#dialRadius# - #moonBorderThickness#)

; =========================
; DEBUG TESTING VARIABLES
; =========================
; Sun / Moon Angles on the Dial (degrees)
sunDialAngle=-1
moonDialAngle=0
; Angle that the illuminated limb of the moon will face (degrees, starts North, counterclockwise)
moonViewAngle=0
; If set to 1, the moon will orient based on the value above. If set to 0, waxing will display on the right, and waning will display on the left.
showMoonAngle=1
; Moon phase value: 0.0 (new) -> 0.5 (full) -> 1.0 (new)
moonPhase=0.12

; ==================================================
; MEASURES
; ==================================================

[MeasureSunDialVisibility]
Measure=Calc
Formula=#sunDialAngle#
IfCondition=(MeasureSunDialVisibility > 180) || (MeasureSunDialVisibility < 0)
IfTrueAction=[!SetVariable sunDialVisibility 0][!UpdateMeter MeterAstroDial][!Redraw]
IfFalseAction=[!SetVariable sunDialVisibility 255][!UpdateMeter MeterAstroDial][!Redraw]
DynamicVariables=1

[MeasureMoonDialVisibility]
Measure=Calc
Formula=#moonDialAngle#
IfCondition=(MeasureMoonDialVisibility > 180) || (MeasureMoonDialVisibility < 0)
IfTrueAction=[!SetVariable moonDialVisibility 0][!UpdateMeter MeterAstroDial][!Redraw]
IfFalseAction=[!SetVariable moonDialVisibility 255][!UpdateMeter MeterAstroDial][!Redraw]
DynamicVariables=1

; Converts the moon phase value into a radius value usable by the arc shape
[MeasureMoonShapePhaseCalc]
Measure=Calc
Formula=clamp(abs((#moonInnerRadius# * 4 * ((#moonPhase# > 0.5) ? (0.5 - (#moonPhase# - 0.5)) : #moonPhase#)) - #moonInnerRadius#),0,#moonInnerRadius#)
DynamicVariables=1

; TEMPORARY DEBUG
[MeasureActionTimer]
Measure=Plugin
Plugin=ActionTimer
ActionList1=Repeat Increase, 16, 180
ActionList2=ResetMove | Repeat Move, 16, 180
ActionList3=ResetMove | Repeat Both, 16, 180
Increase=[!SetVariable moonViewAngle "(#moonViewAngle# + 2)"][!UpdateMeter MeterAstroDial][!UpdateMeasure MeasureActionTimer][!Redraw]
Move=[!SetVariable moonDialAngle "(#moonDialAngle# + 1)"][!UpdateMeter MeterAstroDial][!UpdateMeasure MeasureActionTimer][!Redraw]
Both=[!SetVariable moonViewAngle "(#moonViewAngle# + 2)"][!SetVariable moonDialAngle "(#moonDialAngle# + 1)"][!UpdateMeter MeterAstroDial][!UpdateMeasure MeasureActionTimer][!Redraw]
ResetMove=[!SetVariable moonDialAngle 0][!UpdateMeter MeterAstroDial][!UpdateMeasure MeasureActionTimer][!Redraw]
DynamicVariables=1

; ==================================================
; METERS
; ==================================================

[MeterAstroDial]
Meter=Shape
; Arc
Shape=Arc #dialStartX#,#dialRadius#,(#dialRadius# * 2 + #dialStartX#),#dialRadius#,#dialRadius#,#dialRadius# | StrokeWidth #dialArcThickness# | Stroke Color #colorDialArc# | StrokeStartCap Round | StrokeEndCap Round
; Sun
Shape2=Ellipse #dialStartX#,#dialRadius#,#dialObjectRadius#,#dialObjectRadius# | StrokeWidth 0 | Fill Color #colorSun#,#sunDialVisibility# | Rotate #sunDialAngle#,(#dialRadius# + #dialObjectRadius#),(#dialObjectRadius#)
; Moon
Shape3=Ellipse #dialStartX#,#dialRadius#,#dialObjectRadius#,#dialObjectRadius# | StrokeWidth 0 | Fill Color #colorMoonBorder#,#moonDialVisibility# | Rotate #moonDialAngle#,(#dialRadius# + #dialObjectRadius#),(#dialObjectRadius#)
Shape4=Ellipse #dialStartX#,#dialRadius#,#moonInnerRadius#,#moonInnerRadius# | StrokeWidth 0 | Fill Color #colorMoonBg#,#moonDialVisibility# | Rotate #moonDialAngle#,(#dialRadius# + #moonInnerRadius#),(#moonInnerRadius#)
Shape5=Ellipse #dialStartX#,#dialRadius#,#moonInnerRadius#,#moonInnerRadius# | StrokeWidth 0 | Fill Color #colorMoon#,#moonDialVisibility#
Shape6=Path MoonArc | StrokeWidth 0 | Fill Color #colorMoon# | Rotate (((#showMoonAngle# = 1) ? (-#moonViewAngle#) : ((#moonPhase# > 0.5) ? -90 : 90)) - #moonDialAngle#),#moonInnerRadius#,#moonInnerRadius# | Offset #moonBorderThickness#,#moonBorderThickness# | StrokeLineJoin Round
MoonArc=#moonArcStartX#,#moonArcStartY# | ArcTo (#moonArcStartX# + (#moonInnerRadius# * 2)),#moonArcStartY#,#moonInnerRadius#,[MeasureMoonShapePhaseCalc:],0,(((#moonPhase# > 0.25) && (#moonPhase# < 0.75)) ? 1 : 0) | ArcTo #moonArcStartX#,#moonArcStartY#,#moonInnerRadius#,#moonInnerRadius#,0,1

Shape7=Combine Shape5 | Intersect Shape6 | Rotate #moonDialAngle#,(#dialRadius# + #moonInnerRadius#),(#moonInnerRadius#)

Shape8=Ellipse #dialRadius#,#dialRadius#,5,5 | StrokeWidth 0 | Fill Color 0,150,0

Padding=30,30,30,30
SolidColor=#colorBg#
DynamicVariables=1

[MeterDebugLimbRotation]
Meter=String
FontFace=Arial
FontSize=12
FontColor=230,230,230
Padding=5,5,5,5
SolidColor=#colorBg#
Y=R
Text=Debug Limb Rotation
LeftMouseUpAction=[!CommandMeasure MeasureActionTimer "Execute 1"]
Antialias=1

[MeterDebugDialRotation]
Meter=String
FontFace=Arial
FontSize=12
FontColor=230,230,230
Padding=5,5,5,5
SolidColor=#colorBg#
Y=R
Text=Debug Dial Rotation
LeftMouseUpAction=[!CommandMeasure MeasureActionTimer "Execute 2"]
Antialias=1

[MeterDebugBoth]
Meter=String
FontFace=Arial
FontSize=12
FontColor=230,230,230
Padding=5,5,5,5
SolidColor=#colorBg#
Y=R
Text=Debug Both Rotations
LeftMouseUpAction=[!CommandMeasure MeasureActionTimer "Execute 3"]
Antialias=1
And here is a standalone version of the moon shape, in case it is helpful:

Code: Select all

[Rainmeter]
MiddleMouseUpAction=[!Refresh]
AccurateText=1

[Variables]
; Moon phase value: 0.0 (new) -> 0.5 (full) -> 1.0 (new)
moonPhase=0.0
; Angle that the illuminated limb of the moon will be displayed at (starting at north, going clockwise)
moonViewAngle=0
; Set to 1 to use moonViewAngle, 0 to display horizontally
showMoonAngle=0
; Internal moon variables
moonRadius=16
moonBorderThickness=2
moonInnerRadius=(#moonRadius# - #moonBorderThickness#)
; Colors
colorBg=15,15,15
colorMoon=200,200,200
colorMoonBg=30,30,30
colorMoonBorder=70,70,70

; Converts the moon phase value into a radius value usable by the arc shape
[MeasureMoonShapePhaseCalc]
Measure=Calc
Formula=clamp(abs((#moonInnerRadius# * 4 * ((#moonPhase# > 0.5) ? (0.5 - (#moonPhase# - 0.5)) : #moonPhase#)) - #moonInnerRadius#),0,#moonInnerRadius#)
DynamicVariables=1

[MeterMoonShape]
Meter=Shape
Shape=Ellipse #moonRadius#,#moonRadius#,#moonRadius#,#moonRadius# | StrokeWidth 0 | Fill Color #colorMoonBorder#
Shape2=Ellipse #moonRadius#,#moonRadius#,#moonInnerRadius#,#moonInnerRadius# | StrokeWidth 0 | Fill Color #colorMoonBg#
Shape3=Path MoonArc | StrokeWidth 0 | Fill Color #colorMoon# | Rotate ((#showMoonAngle# = 1) ? (-#moonViewAngle# + 180) : ((#moonPhase# > 0.5) ? -90 : 90)),#moonInnerRadius#,#moonInnerRadius# | Offset #moonBorderThickness#,#moonBorderThickness# | StrokeLineJoin Round
MoonArc=0,#moonInnerRadius# | ArcTo (#moonInnerRadius# * 2),#moonInnerRadius#,#moonInnerRadius#,[MeasureMoonShapePhaseCalc:],0,(((#moonPhase# > 0.25) && (#moonPhase# < 0.75)) ? 1 : 0) | ArcTo 0,#moonInnerRadius#,#moonInnerRadius#,#moonInnerRadius#,0,1
Padding=10,10,10,10
SolidColor=#colorBg#
DynamicVariables=1
LeftMouseUpAction=[!CommandMeasure MeasureMoonShapeActionTimer "Execute 1"]

; Temporary measure for debug purposes - animates between new moon and full moon, then back
[MeasureMoonShapeActionTimer]
Measure=Plugin
Plugin=ActionTimer
ActionList1=Repeat Increase, 16, 100 | Wait 1000 | Repeat Increase, 16, 100 | Reset
Increase=[!SetVariable moonPhase "(#moonPhase# + 0.005)"][!UpdateMeasure MeasureMoonShapePhaseCalc][!UpdateMeter MeterMoonShape][!UpdateMeasure MeasureMoonShapeActionTimer][!Redraw]
Reset=[!SetVariable moonPhase 0][!UpdateMeasure MeasureMoonShapePhaseCalc][!UpdateMeter MeterMoonShape][!UpdateMeasure MeasureMoonShapeActionTimer][!Redraw]
DynamicVariables=1
You do not have the required permissions to view the files attached to this post.
Last edited by raiguard on January 1st, 2019, 1:43 am, edited 1 time in total.
”We are pretty sure that r2922 resolves the regression in resolution caused by a reversion to a revision.” - jsmorley, 2017
User avatar
raiguard
Posts: 630
Joined: June 25th, 2015, 7:02 pm
Location: The Sky, USA

Re: [Lots of Math] Moon Icon Rotation on a Sky Dial

raiguard » December 31st, 2018, 6:12 am

So, I have made some progress! I realized that I could use the sine and cosine of the moon limb's rotation angle to figure out how much the limb's bounding box has moved, and has therefore allowed me to fix the issue:
astrodial less-than-half fixed.gif
It works beautifully!

...NOT.

It only works if the moon phase is less than half a moon!
astrodial more-than-half broken.gif
If the moon phase is more than half a moon, then it behaves completely differently. I can't simply use the trigonometry that I used before, because now instead of being a nice semicircle, the shape is an irregularly shaped ellipse... :headbang:

This would be so much easier if you could somehow retrieve the X and Y positions of shapes within a shape meter, not just the meter itself...

Edit: Or better yet, this would be completely 100% painless if you could 'group' shapes together without actually combining them, so I could move the entire moon icon as a single unit, rather than as three separate shapes.

Anyone have any input?

AstroDial.ini:

Code: Select all

[Rainmeter]
MiddleMouseUpAction=[!Refresh]
AccurateText=1

[Variables]
; Colors
colorBg=15,15,15
colorDialArc=150,150,150
colorMoon=200,200,200
colorMoonBg=30,30,30
colorMoonBorder=70,70,70
colorSun=250,222,110
; Dial settings
dialRadius=250
dialStartX=3
dialArcThickness=6
dialObjectRadius=23
; Moon settings
moonBorderThickness=3
moonInnerRadius=(#dialObjectRadius# - #moonBorderThickness#)
moonArcStartX=(#dialStartX# - #dialObjectRadius#)
moonArcStartY=(#dialRadius# - #moonBorderThickness#)
moonViewAngle=((#rawMVA# + #moonDialAngle#) % 360)

; =========================
; DEBUG TESTING VARIABLES
; =========================
; Sun / Moon Angles on the Dial (degrees)
sunDialAngle=-1
moonDialAngle=0
; Angle that the illuminated limb of the moon will face (degrees, starts North, counterclockwise)
rawMVA=0
; If set to 1, the moon will orient based on the value above. If set to 0, waxing will display on the right, and waning will display on the left.
showMoonAngle=1
; Moon phase value: 0.0 (new) -> 0.5 (full) -> 1.0 (new)
moonPhase=0.12

; ==================================================
; MEASURES
; ==================================================

[MeasureSunDialVisibility]
Measure=Calc
Formula=#sunDialAngle#
IfCondition=(MeasureSunDialVisibility > 180) || (MeasureSunDialVisibility < 0)
IfTrueAction=[!SetVariable sunDialVisibility 0][!UpdateMeter MeterAstroDial][!Redraw]
IfFalseAction=[!SetVariable sunDialVisibility 255][!UpdateMeter MeterAstroDial][!Redraw]
DynamicVariables=1

[MeasureMoonDialVisibility]
Measure=Calc
Formula=#moonDialAngle#
IfCondition=(MeasureMoonDialVisibility > 180) || (MeasureMoonDialVisibility < 0)
IfTrueAction=[!SetVariable moonDialVisibility 0][!UpdateMeter MeterAstroDial][!Redraw]
IfFalseAction=[!SetVariable moonDialVisibility 255][!UpdateMeter MeterAstroDial][!Redraw]
DynamicVariables=1

; Converts the moon phase value into a radius value usable by the arc shape
[MeasureMoonShapePhaseCalc]
Measure=Calc
Formula=clamp(abs((#moonInnerRadius# * 4 * ((#moonPhase# > 0.5) ? (0.5 - (#moonPhase# - 0.5)) : #moonPhase#)) - #moonInnerRadius#),0,#moonInnerRadius#)
DynamicVariables=1
Group=MoonShape

; Adjusts the center of arc rotation for the moon limb shape in the X direction
[MeasureMoonLimbArcRotationXOffsetCalc]
Measure=Calc
DynamicVariables=1
Formula=(((#moonViewAngle# > 180) && (#moonViewAngle# < 360)) ? (#moonInnerRadius# * abs(cos(rad(#moonViewAngle#)))) : #moonInnerRadius#)
Group=MoonShape

; Adjusts the center of arc rotation for the moon limb shape in the Y direction
[MeasureMoonLimbArcRotationYOffsetCalc]
Measure=Calc
DynamicVariables=1
Formula=(((#moonViewAngle# <= 90) || (#moonViewAngle# >= 270)) ? #moonInnerRadius# : (#moonInnerRadius# * abs(sin(rad(#moonViewAngle#)))))
Group=MoonShape

; TEMPORARY DEBUG - rotates the moon's illuminated limb 360 degrees
[MeasureActionTimer]
Measure=Plugin
Plugin=ActionTimer
ActionList1=Repeat Increase, 16, 180
ActionList2=ResetMove | Repeat Move, 16, 180
ActionList3=ResetMove | Repeat Both, 16, 180
ActionList4=Repeat Increase, 16, 5
Increase=[!SetVariable rawMVA "(#rawMVA# + 2)"][!SetVariable moonViewAngle "((#rawMVA# + #moonDialAngle#) % 360)"][!UpdateMeasureGroup MoonShape][!UpdateMeter MeterAstroDial][!UpdateMeasure MeasureActionTimer][!Redraw]
Move=[!SetVariable moonDialAngle "(#moonDialAngle# + 1)"][!SetVariable moonViewAngle "((#rawMVA# + #moonDialAngle#) % 360)"][!UpdateMeasureGroup MoonShape][!UpdateMeter MeterAstroDial][!UpdateMeasure MeasureActionTimer][!Redraw]
Both=[!SetVariable rawMVA "(#rawMVA# + 2)"][!SetVariable moonDialAngle "(#moonDialAngle# + 1)"][!SetVariable moonViewAngle "((#rawMVA# + #moonDialAngle#) % 360)"][!UpdateMeasureGroup MoonShape][!UpdateMeter MeterAstroDial][!UpdateMeasure MeasureActionTimer][!Redraw]
ResetMove=[!SetVariable moonDialAngle 0][!UpdateMeasureGroup MoonShape][!UpdateMeter MeterAstroDial][!UpdateMeasure MeasureActionTimer][!Redraw]
DynamicVariables=1

; ==================================================
; METERS
; ==================================================

[MeterAstroDial]
Meter=Shape
; Arc
Shape=Arc #dialStartX#,#dialRadius#,(#dialRadius# * 2 + #dialStartX#),#dialRadius#,#dialRadius#,#dialRadius# | StrokeWidth #dialArcThickness# | Stroke Color #colorDialArc# | StrokeStartCap Round | StrokeEndCap Round
; Sun
Shape2=Ellipse #dialStartX#,#dialRadius#,#dialObjectRadius#,#dialObjectRadius# | StrokeWidth 0 | Fill Color #colorSun#,#sunDialVisibility# | Rotate #sunDialAngle#,(#dialRadius# + #dialObjectRadius#),(#dialObjectRadius#)
; Moon
Shape3=Ellipse #dialStartX#,#dialRadius#,#dialObjectRadius#,#dialObjectRadius# | StrokeWidth 0 | Fill Color #colorMoonBorder#,#moonDialVisibility# | Rotate #moonDialAngle#,(#dialRadius# + #dialObjectRadius#),(#dialObjectRadius#)
Shape4=Ellipse #dialStartX#,#dialRadius#,#moonInnerRadius#,#moonInnerRadius# | StrokeWidth 0 | Fill Color #colorMoonBg#,#moonDialVisibility# | Rotate #moonDialAngle#,(#dialRadius# + #moonInnerRadius#),(#moonInnerRadius#)
Shape5=Ellipse #dialStartX#,#dialRadius#,#moonInnerRadius#,#moonInnerRadius# | StrokeWidth 0 | Fill Color #colorMoon#,#moonDialVisibility#
Shape6=Path MoonArc | StrokeWidth 0 | Fill Color #colorMoon# | Rotate (((#showMoonAngle# = 1) ? (-#moonViewAngle#) : ((#moonPhase# > 0.5) ? -90 : 90))),#moonInnerRadius#,#moonInnerRadius# | Offset #moonBorderThickness#,#moonBorderThickness# | StrokeLineJoin Round
MoonArc=#moonArcStartX#,#moonArcStartY# | ArcTo (#moonArcStartX# + (#moonInnerRadius# * 2)),#moonArcStartY#,#moonInnerRadius#,[MeasureMoonShapePhaseCalc:],0,(((#moonPhase# > 0.25) && (#moonPhase# < 0.75)) ? 1 : 0) | ArcTo #moonArcStartX#,#moonArcStartY#,#moonInnerRadius#,#moonInnerRadius#,0,1

Shape7=Combine Shape5 | Intersect Shape6 | Rotate #moonDialAngle#,(#dialRadius# + [MeasureMoonLimbArcRotationXOffsetCalc:]),([MeasureMoonLimbArcRotationYOffsetCalc:])

Shape8=Ellipse #dialRadius#,#dialRadius#,5,5 | StrokeWidth 0 | Fill Color 0,150,0

Padding=30,30,30,30
SolidColor=#colorBg#
DynamicVariables=1

[MeterDebugLimbRotation]
Meter=String
FontFace=Arial
FontSize=12
FontColor=230,230,230
Padding=5,5,5,5
SolidColor=#colorBg#
Y=R
Text=Debug Limb Rotation
LeftMouseUpAction=[!CommandMeasure MeasureActionTimer "Execute 1"]
Antialias=1

[MeterDebugDialRotation]
Meter=String
FontFace=Arial
FontSize=12
FontColor=230,230,230
Padding=5,5,5,5
SolidColor=#colorBg#
Y=R
Text=Debug Dial Rotation
LeftMouseUpAction=[!CommandMeasure MeasureActionTimer "Execute 2"]
Antialias=1

[MeterDebugBoth]
Meter=String
FontFace=Arial
FontSize=12
FontColor=230,230,230
Padding=5,5,5,5
SolidColor=#colorBg#
Y=R
Text=Debug Both Rotations
LeftMouseUpAction=[!CommandMeasure MeasureActionTimer "Execute 3"]
Antialias=1

[MeterDebugStepLimb]
Meter=String
FontFace=Arial
FontSize=12
FontColor=230,230,230
Padding=5,5,5,5
SolidColor=#colorBg#
Y=R
Text=Debug Step Limb Rotation
LeftMouseUpAction=[!CommandMeasure MeasureActionTimer "Execute 4"]
Antialias=1
You do not have the required permissions to view the files attached to this post.
”We are pretty sure that r2922 resolves the regression in resolution caused by a reversion to a revision.” - jsmorley, 2017
SparkShredder
Posts: 50
Joined: November 17th, 2017, 12:00 pm

Re: [Lots of Math] Moon Icon Rotation on a Sky Dial

SparkShredder » December 31st, 2018, 8:26 am

Honestly, I'm not that of a pro coder, So, I'd have taken help from Photoshop.
I may create various icons and and leave transparent space on right side
and Used:
Meter=Rotator
MeasureName=HowMuchItShouldRotate
Image?=[WhichIconItshouldUseMeasure]
???Rotate About Center-RightMost Corner Codes???

Blah Blah Blah
User avatar
GTI.H
Posts: 72
Joined: December 15th, 2018, 3:35 am

Re: [Lots of Math] Moon Icon Rotation on a Sky Dial

GTI.H » December 31st, 2018, 4:00 pm

Hi raiguard,

I like math and am currently interested in orbital movements with Rainmeter.

I've been looking at your code in my spare time, but I do not know if I understood your wish. For me to understand your math I need to first study and familiarize with measures Shape and ActionTimer, but I can already feel the lack of trigonometric functions to solve a trigonometric issue.

I suspect that you have rewrote part of trigonometry (eg. x.cos=y) and that the error in AstroDial.ini starts in the second quadrant which leads us to suspect one of its conditions in Shape6 = Path MoonArc

To solve this issue, the first way that comes to my mind is to use a meter Rotator moving with Sin/Cos functions by setting its X and Y positions.

--
Regards
GTI
raiguard wrote:
December 30th, 2018, 3:22 am
Hello everyone. I am attempting to create a sky dial using shape meters, and while I have done a basic one before, the one I am currently creating is very complicated and convoluted. I am creating this thread because I am bound to have many math-related issues with path shapes.

Here is my first problem:

For the moon icon, I would like to be able to display the current moon phase, as well as the angle of the illuminated side as you would see it in the sky. I have done both of these things before, that is not my problem. My problem arises when I attempt to implement them on the sky dial:

atrodial PROBLEMS.gif

That's not right at all!

My only hypothesis so far is that when the moon limb shape (the white illuminated part) is at certain angles, it's changing where the top-left of the limb's bounding box is, and therefore, the arc rotation anchor point moves as well. However, I have absolutely no clue how to fix it. I need to somehow create a function of the moon's limb rotation that will move the arc rotation anchor back to where it is supposed to be (signified by the green dot).

Does anybody have any ideas? Much appreciated. :D

AstroDial.ini:

Code: Select all

[Rainmeter]
MiddleMouseUpAction=[!Refresh]
AccurateText=1

[Variables]
; Colors
colorBg=15,15,15
colorDialArc=150,150,150
colorMoon=200,200,200
colorMoonBg=30,30,30
colorMoonBorder=70,70,70
colorSun=250,222,110
; Dial settings
dialRadius=250
dialStartX=3
dialArcThickness=6
dialObjectRadius=23
; Moon settings
moonBorderThickness=3
moonInnerRadius=(#dialObjectRadius# - #moonBorderThickness#)
moonArcStartX=(#dialStartX# - #dialObjectRadius#)
moonArcStartY=(#dialRadius# - #moonBorderThickness#)

; =========================
; DEBUG TESTING VARIABLES
; =========================
; Sun / Moon Angles on the Dial (degrees)
sunDialAngle=-1
moonDialAngle=0
; Angle that the illuminated limb of the moon will face (degrees, starts North, counterclockwise)
moonViewAngle=0
; If set to 1, the moon will orient based on the value above. If set to 0, waxing will display on the right, and waning will display on the left.
showMoonAngle=1
; Moon phase value: 0.0 (new) -> 0.5 (full) -> 1.0 (new)
moonPhase=0.12

; ==================================================
; MEASURES
; ==================================================

[MeasureSunDialVisibility]
Measure=Calc
Formula=#sunDialAngle#
IfCondition=(MeasureSunDialVisibility > 180) || (MeasureSunDialVisibility < 0)
IfTrueAction=[!SetVariable sunDialVisibility 0][!UpdateMeter MeterAstroDial][!Redraw]
IfFalseAction=[!SetVariable sunDialVisibility 255][!UpdateMeter MeterAstroDial][!Redraw]
DynamicVariables=1

[MeasureMoonDialVisibility]
Measure=Calc
Formula=#moonDialAngle#
IfCondition=(MeasureMoonDialVisibility > 180) || (MeasureMoonDialVisibility < 0)
IfTrueAction=[!SetVariable moonDialVisibility 0][!UpdateMeter MeterAstroDial][!Redraw]
IfFalseAction=[!SetVariable moonDialVisibility 255][!UpdateMeter MeterAstroDial][!Redraw]
DynamicVariables=1

; Converts the moon phase value into a radius value usable by the arc shape
[MeasureMoonShapePhaseCalc]
Measure=Calc
Formula=clamp(abs((#moonInnerRadius# * 4 * ((#moonPhase# > 0.5) ? (0.5 - (#moonPhase# - 0.5)) : #moonPhase#)) - #moonInnerRadius#),0,#moonInnerRadius#)
DynamicVariables=1

; TEMPORARY DEBUG
[MeasureActionTimer]
Measure=Plugin
Plugin=ActionTimer
ActionList1=Repeat Increase, 16, 180
ActionList2=ResetMove | Repeat Move, 16, 180
ActionList3=ResetMove | Repeat Both, 16, 180
Increase=[!SetVariable moonViewAngle "(#moonViewAngle# + 2)"][!UpdateMeter MeterAstroDial][!UpdateMeasure MeasureActionTimer][!Redraw]
Move=[!SetVariable moonDialAngle "(#moonDialAngle# + 1)"][!UpdateMeter MeterAstroDial][!UpdateMeasure MeasureActionTimer][!Redraw]
Both=[!SetVariable moonViewAngle "(#moonViewAngle# + 2)"][!SetVariable moonDialAngle "(#moonDialAngle# + 1)"][!UpdateMeter MeterAstroDial][!UpdateMeasure MeasureActionTimer][!Redraw]
ResetMove=[!SetVariable moonDialAngle 0][!UpdateMeter MeterAstroDial][!UpdateMeasure MeasureActionTimer][!Redraw]
DynamicVariables=1

; ==================================================
; METERS
; ==================================================

[MeterAstroDial]
Meter=Shape
; Arc
Shape=Arc #dialStartX#,#dialRadius#,(#dialRadius# * 2 + #dialStartX#),#dialRadius#,#dialRadius#,#dialRadius# | StrokeWidth #dialArcThickness# | Stroke Color #colorDialArc# | StrokeStartCap Round | StrokeEndCap Round
; Sun
Shape2=Ellipse #dialStartX#,#dialRadius#,#dialObjectRadius#,#dialObjectRadius# | StrokeWidth 0 | Fill Color #colorSun#,#sunDialVisibility# | Rotate #sunDialAngle#,(#dialRadius# + #dialObjectRadius#),(#dialObjectRadius#)
; Moon
Shape3=Ellipse #dialStartX#,#dialRadius#,#dialObjectRadius#,#dialObjectRadius# | StrokeWidth 0 | Fill Color #colorMoonBorder#,#moonDialVisibility# | Rotate #moonDialAngle#,(#dialRadius# + #dialObjectRadius#),(#dialObjectRadius#)
Shape4=Ellipse #dialStartX#,#dialRadius#,#moonInnerRadius#,#moonInnerRadius# | StrokeWidth 0 | Fill Color #colorMoonBg#,#moonDialVisibility# | Rotate #moonDialAngle#,(#dialRadius# + #moonInnerRadius#),(#moonInnerRadius#)
Shape5=Ellipse #dialStartX#,#dialRadius#,#moonInnerRadius#,#moonInnerRadius# | StrokeWidth 0 | Fill Color #colorMoon#,#moonDialVisibility#
Shape6=Path MoonArc | StrokeWidth 0 | Fill Color #colorMoon# | Rotate (((#showMoonAngle# = 1) ? (-#moonViewAngle#) : ((#moonPhase# > 0.5) ? -90 : 90)) - #moonDialAngle#),#moonInnerRadius#,#moonInnerRadius# | Offset #moonBorderThickness#,#moonBorderThickness# | StrokeLineJoin Round
MoonArc=#moonArcStartX#,#moonArcStartY# | ArcTo (#moonArcStartX# + (#moonInnerRadius# * 2)),#moonArcStartY#,#moonInnerRadius#,[MeasureMoonShapePhaseCalc:],0,(((#moonPhase# > 0.25) && (#moonPhase# < 0.75)) ? 1 : 0) | ArcTo #moonArcStartX#,#moonArcStartY#,#moonInnerRadius#,#moonInnerRadius#,0,1

Shape7=Combine Shape5 | Intersect Shape6 | Rotate #moonDialAngle#,(#dialRadius# + #moonInnerRadius#),(#moonInnerRadius#)

Shape8=Ellipse #dialRadius#,#dialRadius#,5,5 | StrokeWidth 0 | Fill Color 0,150,0

Padding=30,30,30,30
SolidColor=#colorBg#
DynamicVariables=1

[MeterDebugLimbRotation]
Meter=String
FontFace=Arial
FontSize=12
FontColor=230,230,230
Padding=5,5,5,5
SolidColor=#colorBg#
Y=R
Text=Debug Limb Rotation
LeftMouseUpAction=[!CommandMeasure MeasureActionTimer "Execute 1"]
Antialias=1

[MeterDebugDialRotation]
Meter=String
FontFace=Arial
FontSize=12
FontColor=230,230,230
Padding=5,5,5,5
SolidColor=#colorBg#
Y=R
Text=Debug Dial Rotation
LeftMouseUpAction=[!CommandMeasure MeasureActionTimer "Execute 2"]
Antialias=1

[MeterDebugBoth]
Meter=String
FontFace=Arial
FontSize=12
FontColor=230,230,230
Padding=5,5,5,5
SolidColor=#colorBg#
Y=R
Text=Debug Both Rotations
LeftMouseUpAction=[!CommandMeasure MeasureActionTimer "Execute 3"]
Antialias=1
And here is a standalone version of the moon shape, in case it is helpful:

Code: Select all

[Rainmeter]
MiddleMouseUpAction=[!Refresh]
AccurateText=1

[Variables]
; Moon phase value: 0.0 (new) -> 0.5 (full) -> 1.0 (new)
moonPhase=0.0
; Angle that the illuminated limb of the moon will be displayed at (starting at north, going clockwise)
moonViewAngle=0
; Set to 1 to use moonViewAngle, 0 to display horizontally
showMoonAngle=0
; Internal moon variables
moonRadius=16
moonBorderThickness=2
moonInnerRadius=(#moonRadius# - #moonBorderThickness#)
; Colors
colorBg=15,15,15
colorMoon=200,200,200
colorMoonBg=30,30,30
colorMoonBorder=70,70,70

; Converts the moon phase value into a radius value usable by the arc shape
[MeasureMoonShapePhaseCalc]
Measure=Calc
Formula=clamp(abs((#moonInnerRadius# * 4 * ((#moonPhase# > 0.5) ? (0.5 - (#moonPhase# - 0.5)) : #moonPhase#)) - #moonInnerRadius#),0,#moonInnerRadius#)
DynamicVariables=1

[MeterMoonShape]
Meter=Shape
Shape=Ellipse #moonRadius#,#moonRadius#,#moonRadius#,#moonRadius# | StrokeWidth 0 | Fill Color #colorMoonBorder#
Shape2=Ellipse #moonRadius#,#moonRadius#,#moonInnerRadius#,#moonInnerRadius# | StrokeWidth 0 | Fill Color #colorMoonBg#
Shape3=Path MoonArc | StrokeWidth 0 | Fill Color #colorMoon# | Rotate ((#showMoonAngle# = 1) ? (-#moonViewAngle# + 180) : ((#moonPhase# > 0.5) ? -90 : 90)),#moonInnerRadius#,#moonInnerRadius# | Offset #moonBorderThickness#,#moonBorderThickness# | StrokeLineJoin Round
MoonArc=0,#moonInnerRadius# | ArcTo (#moonInnerRadius# * 2),#moonInnerRadius#,#moonInnerRadius#,[MeasureMoonShapePhaseCalc:],0,(((#moonPhase# > 0.25) && (#moonPhase# < 0.75)) ? 1 : 0) | ArcTo 0,#moonInnerRadius#,#moonInnerRadius#,#moonInnerRadius#,0,1
Padding=10,10,10,10
SolidColor=#colorBg#
DynamicVariables=1
LeftMouseUpAction=[!CommandMeasure MeasureMoonShapeActionTimer "Execute 1"]

; Temporary measure for debug purposes - animates between new moon and full moon, then back
[MeasureMoonShapeActionTimer]
Measure=Plugin
Plugin=ActionTimer
ActionList1=Repeat Increase, 16, 100 | Wait 1000 | Repeat Increase, 16, 100 | Reset
Increase=[!SetVariable moonPhase "(#moonPhase# + 0.005)"][!UpdateMeasure MeasureMoonShapePhaseCalc][!UpdateMeter MeterMoonShape][!UpdateMeasure MeasureMoonShapeActionTimer][!Redraw]
Reset=[!SetVariable moonPhase 0][!UpdateMeasure MeasureMoonShapePhaseCalc][!UpdateMeter MeterMoonShape][!UpdateMeasure MeasureMoonShapeActionTimer][!Redraw]
DynamicVariables=1
Last edited by GTI.H on December 31st, 2018, 5:56 pm, edited 2 times in total.
User avatar
GTI.H
Posts: 72
Joined: December 15th, 2018, 3:35 am

Re: [Lots of Math] Moon Icon Rotation on a Sky Dial

GTI.H » December 31st, 2018, 5:54 pm

Oh . . . Great, Good job! :thumbup:

Thank you for sharing the code, went through my head in making a skin similar to yours for any Astros (Moon/Sun/Earth/Etc.).

This would be better if the beginning and end of the arc represented the rising and setting of the Astro and that the arc represented the true orbit of the Astro and for this we need the equation that describes the orbit of the Astro!
It would be nice to have the equation that describes the phases of the moon, do you have this equation?

As I said before:
To solve this issue, the first way that come to my mind is to use meter Rotator moving with Sin/Cos functions by setting its X and Y positions.

--
Regards
GTI
raiguard wrote:
December 31st, 2018, 6:12 am
So, I have made some progress! I realized that I could use the sine and cosine of the moon limb's rotation angle to figure out how much the limb's bounding box has moved, and has therefore allowed me to fix the issue:

astrodial less-than-half fixed.gif

It works beautifully!

...NOT.

It only works if the moon phase is less than half a moon!

astrodial more-than-half broken.gif
If the moon phase is more than half a moon, then it behaves completely differently. I can't simply use the trigonometry that I used before, because now instead of being a nice semicircle, the shape is an irregularly shaped ellipse... :headbang:

This would be so much easier if you could somehow retrieve the X and Y positions of shapes within a shape meter, not just the meter itself...

Edit: Or better yet, this would be completely 100% painless if you could 'group' shapes together without actually combining them, so I could move the entire moon icon as a single unit, rather than as three separate shapes.

Anyone have any input?

AstroDial.ini:

Code: Select all

[Rainmeter]
MiddleMouseUpAction=[!Refresh]
AccurateText=1

[Variables]
; Colors
colorBg=15,15,15
colorDialArc=150,150,150
colorMoon=200,200,200
colorMoonBg=30,30,30
colorMoonBorder=70,70,70
colorSun=250,222,110
; Dial settings
dialRadius=250
dialStartX=3
dialArcThickness=6
dialObjectRadius=23
; Moon settings
moonBorderThickness=3
moonInnerRadius=(#dialObjectRadius# - #moonBorderThickness#)
moonArcStartX=(#dialStartX# - #dialObjectRadius#)
moonArcStartY=(#dialRadius# - #moonBorderThickness#)
moonViewAngle=((#rawMVA# + #moonDialAngle#) % 360)

; =========================
; DEBUG TESTING VARIABLES
; =========================
; Sun / Moon Angles on the Dial (degrees)
sunDialAngle=-1
moonDialAngle=0
; Angle that the illuminated limb of the moon will face (degrees, starts North, counterclockwise)
rawMVA=0
; If set to 1, the moon will orient based on the value above. If set to 0, waxing will display on the right, and waning will display on the left.
showMoonAngle=1
; Moon phase value: 0.0 (new) -> 0.5 (full) -> 1.0 (new)
moonPhase=0.12

; ==================================================
; MEASURES
; ==================================================

[MeasureSunDialVisibility]
Measure=Calc
Formula=#sunDialAngle#
IfCondition=(MeasureSunDialVisibility > 180) || (MeasureSunDialVisibility < 0)
IfTrueAction=[!SetVariable sunDialVisibility 0][!UpdateMeter MeterAstroDial][!Redraw]
IfFalseAction=[!SetVariable sunDialVisibility 255][!UpdateMeter MeterAstroDial][!Redraw]
DynamicVariables=1

[MeasureMoonDialVisibility]
Measure=Calc
Formula=#moonDialAngle#
IfCondition=(MeasureMoonDialVisibility > 180) || (MeasureMoonDialVisibility < 0)
IfTrueAction=[!SetVariable moonDialVisibility 0][!UpdateMeter MeterAstroDial][!Redraw]
IfFalseAction=[!SetVariable moonDialVisibility 255][!UpdateMeter MeterAstroDial][!Redraw]
DynamicVariables=1

; Converts the moon phase value into a radius value usable by the arc shape
[MeasureMoonShapePhaseCalc]
Measure=Calc
Formula=clamp(abs((#moonInnerRadius# * 4 * ((#moonPhase# > 0.5) ? (0.5 - (#moonPhase# - 0.5)) : #moonPhase#)) - #moonInnerRadius#),0,#moonInnerRadius#)
DynamicVariables=1
Group=MoonShape

; Adjusts the center of arc rotation for the moon limb shape in the X direction
[MeasureMoonLimbArcRotationXOffsetCalc]
Measure=Calc
DynamicVariables=1
Formula=(((#moonViewAngle# > 180) && (#moonViewAngle# < 360)) ? (#moonInnerRadius# * abs(cos(rad(#moonViewAngle#)))) : #moonInnerRadius#)
Group=MoonShape

; Adjusts the center of arc rotation for the moon limb shape in the Y direction
[MeasureMoonLimbArcRotationYOffsetCalc]
Measure=Calc
DynamicVariables=1
Formula=(((#moonViewAngle# <= 90) || (#moonViewAngle# >= 270)) ? #moonInnerRadius# : (#moonInnerRadius# * abs(sin(rad(#moonViewAngle#)))))
Group=MoonShape

; TEMPORARY DEBUG - rotates the moon's illuminated limb 360 degrees
[MeasureActionTimer]
Measure=Plugin
Plugin=ActionTimer
ActionList1=Repeat Increase, 16, 180
ActionList2=ResetMove | Repeat Move, 16, 180
ActionList3=ResetMove | Repeat Both, 16, 180
ActionList4=Repeat Increase, 16, 5
Increase=[!SetVariable rawMVA "(#rawMVA# + 2)"][!SetVariable moonViewAngle "((#rawMVA# + #moonDialAngle#) % 360)"][!UpdateMeasureGroup MoonShape][!UpdateMeter MeterAstroDial][!UpdateMeasure MeasureActionTimer][!Redraw]
Move=[!SetVariable moonDialAngle "(#moonDialAngle# + 1)"][!SetVariable moonViewAngle "((#rawMVA# + #moonDialAngle#) % 360)"][!UpdateMeasureGroup MoonShape][!UpdateMeter MeterAstroDial][!UpdateMeasure MeasureActionTimer][!Redraw]
Both=[!SetVariable rawMVA "(#rawMVA# + 2)"][!SetVariable moonDialAngle "(#moonDialAngle# + 1)"][!SetVariable moonViewAngle "((#rawMVA# + #moonDialAngle#) % 360)"][!UpdateMeasureGroup MoonShape][!UpdateMeter MeterAstroDial][!UpdateMeasure MeasureActionTimer][!Redraw]
ResetMove=[!SetVariable moonDialAngle 0][!UpdateMeasureGroup MoonShape][!UpdateMeter MeterAstroDial][!UpdateMeasure MeasureActionTimer][!Redraw]
DynamicVariables=1

; ==================================================
; METERS
; ==================================================

[MeterAstroDial]
Meter=Shape
; Arc
Shape=Arc #dialStartX#,#dialRadius#,(#dialRadius# * 2 + #dialStartX#),#dialRadius#,#dialRadius#,#dialRadius# | StrokeWidth #dialArcThickness# | Stroke Color #colorDialArc# | StrokeStartCap Round | StrokeEndCap Round
; Sun
Shape2=Ellipse #dialStartX#,#dialRadius#,#dialObjectRadius#,#dialObjectRadius# | StrokeWidth 0 | Fill Color #colorSun#,#sunDialVisibility# | Rotate #sunDialAngle#,(#dialRadius# + #dialObjectRadius#),(#dialObjectRadius#)
; Moon
Shape3=Ellipse #dialStartX#,#dialRadius#,#dialObjectRadius#,#dialObjectRadius# | StrokeWidth 0 | Fill Color #colorMoonBorder#,#moonDialVisibility# | Rotate #moonDialAngle#,(#dialRadius# + #dialObjectRadius#),(#dialObjectRadius#)
Shape4=Ellipse #dialStartX#,#dialRadius#,#moonInnerRadius#,#moonInnerRadius# | StrokeWidth 0 | Fill Color #colorMoonBg#,#moonDialVisibility# | Rotate #moonDialAngle#,(#dialRadius# + #moonInnerRadius#),(#moonInnerRadius#)
Shape5=Ellipse #dialStartX#,#dialRadius#,#moonInnerRadius#,#moonInnerRadius# | StrokeWidth 0 | Fill Color #colorMoon#,#moonDialVisibility#
Shape6=Path MoonArc | StrokeWidth 0 | Fill Color #colorMoon# | Rotate (((#showMoonAngle# = 1) ? (-#moonViewAngle#) : ((#moonPhase# > 0.5) ? -90 : 90))),#moonInnerRadius#,#moonInnerRadius# | Offset #moonBorderThickness#,#moonBorderThickness# | StrokeLineJoin Round
MoonArc=#moonArcStartX#,#moonArcStartY# | ArcTo (#moonArcStartX# + (#moonInnerRadius# * 2)),#moonArcStartY#,#moonInnerRadius#,[MeasureMoonShapePhaseCalc:],0,(((#moonPhase# > 0.25) && (#moonPhase# < 0.75)) ? 1 : 0) | ArcTo #moonArcStartX#,#moonArcStartY#,#moonInnerRadius#,#moonInnerRadius#,0,1

Shape7=Combine Shape5 | Intersect Shape6 | Rotate #moonDialAngle#,(#dialRadius# + [MeasureMoonLimbArcRotationXOffsetCalc:]),([MeasureMoonLimbArcRotationYOffsetCalc:])

Shape8=Ellipse #dialRadius#,#dialRadius#,5,5 | StrokeWidth 0 | Fill Color 0,150,0

Padding=30,30,30,30
SolidColor=#colorBg#
DynamicVariables=1

[MeterDebugLimbRotation]
Meter=String
FontFace=Arial
FontSize=12
FontColor=230,230,230
Padding=5,5,5,5
SolidColor=#colorBg#
Y=R
Text=Debug Limb Rotation
LeftMouseUpAction=[!CommandMeasure MeasureActionTimer "Execute 1"]
Antialias=1

[MeterDebugDialRotation]
Meter=String
FontFace=Arial
FontSize=12
FontColor=230,230,230
Padding=5,5,5,5
SolidColor=#colorBg#
Y=R
Text=Debug Dial Rotation
LeftMouseUpAction=[!CommandMeasure MeasureActionTimer "Execute 2"]
Antialias=1

[MeterDebugBoth]
Meter=String
FontFace=Arial
FontSize=12
FontColor=230,230,230
Padding=5,5,5,5
SolidColor=#colorBg#
Y=R
Text=Debug Both Rotations
LeftMouseUpAction=[!CommandMeasure MeasureActionTimer "Execute 3"]
Antialias=1

[MeterDebugStepLimb]
Meter=String
FontFace=Arial
FontSize=12
FontColor=230,230,230
Padding=5,5,5,5
SolidColor=#colorBg#
Y=R
Text=Debug Step Limb Rotation
LeftMouseUpAction=[!CommandMeasure MeasureActionTimer "Execute 4"]
Antialias=1
User avatar
raiguard
Posts: 630
Joined: June 25th, 2015, 7:02 pm
Location: The Sky, USA

Re: [Lots of Math] Moon Icon Rotation on a Sky Dial

raiguard » December 31st, 2018, 8:14 pm

SparkShredder wrote:
December 31st, 2018, 8:26 am
Honestly, I'm not that of a pro coder, So, I'd have taken help from Photoshop.
I may create various icons and and leave transparent space on right side
and Used:
Meter=Rotator
MeasureName=HowMuchItShouldRotate
Image?=[WhichIconItshouldUseMeasure]
???Rotate About Center-RightMost Corner Codes???

Blah Blah Blah
Unfortunately, since I am using a shape meter, using a rotator meter is not an option to me. I don't want to use static images for the moon phase - I have a script that very accurately calculates the moon phase, and I created a shape meter that can dynamically show that phase without needing to use images.

Thanks for the input though!
”We are pretty sure that r2922 resolves the regression in resolution caused by a reversion to a revision.” - jsmorley, 2017
User avatar
raiguard
Posts: 630
Joined: June 25th, 2015, 7:02 pm
Location: The Sky, USA

Re: [Lots of Math] Moon Icon Rotation on a Sky Dial

raiguard » December 31st, 2018, 8:36 pm

GTI.H wrote:
December 31st, 2018, 4:00 pm
Hi raiguard,

I like math and am currently interested in orbital movements with Rainmeter.

I've been looking at your code in my spare time, but I do not know if I understood your wish. For me to understand your math I need to first study and familiarize with measures Shape and ActionTimer, but I can already feel the lack of trigonometric functions to solve a trigonometric issue.

I suspect that you have rewrote part of trigonometry (eg. x.cos=y) and that the error in AstroDial.ini starts in the second quadrant which leads us to suspect one of its conditions in Shape6 = Path MoonArc

To solve this issue, the first way that comes to my mind is to use a meter Rotator moving with Sin/Cos functions by setting its X and Y positions.

--
Regards
GTI
Unfortunately, once again, I can't use a rotator meter here. My moon icon is a dynamically created shape that matches a moon phase value from a LUA script. If I were to use a rotator meter, I would need to use images, which I do not want to do.

I'll fire up MS paint and try to explain the exact issue I'm encountering in more detail, so you (and others) can analyse it properly. Stay tuned.
GTI.H wrote:
December 31st, 2018, 5:54 pm
Oh . . . Great, Good job! :thumbup:

Thank you for sharing the code, went through my head in making a skin similar to yours for any Astros (Moon/Sun/Earth/Etc.).

This would be better if the beginning and end of the arc represented the rising and setting of the Astro and that the arc represented the true orbit of the Astro and for this we need the equation that describes the orbit of the Astro!
It would be nice to have the equation that describes the phases of the moon, do you have this equation?

As I said before:
To solve this issue, the first way that come to my mind is to use meter Rotator moving with Sin/Cos functions by setting its X and Y positions.

--
Regards
GTI
As it currently stands, the arc will represent the actual rising/setting of the sun and moon. It will not display the actual orbit of the objects, but simply be a representation of elapsed time between rising/setting. My eventual plan is to also represent the times for dawn/dusk, golden hours, meridian crossings, etc. in another arc below the dial. All of this information is gathered through a LUA script that I translated from JavaScript, called SunCalc. You can find the original JavaScript code here

Here is the script translated to LUA, with additions to make it work with Rainmeter skins:

Code: Select all

--[[
    ----------------------------------------------------------------------------------------------------
    SUNCALC.LUA
    raiguard
    v1.0.5

    This script is a form of 'SunCalc' by mourner, translated to LUA and adapted for Rainmeter
    The original source code of SunCalc can be found at https://github.com/mourner/suncalc
    See below to view SunCalc's source code license
    ----------------------------------------------------------------------------------------------------
    CHANGELOG:
    v1.0.5 - 2018-12-30
        - Removed code from the Update() function to facilitate invoking the script multiple times
          with different parameters through !CommandMeasure bangs
        - Updated documentation
    v1.0.4 - 2018-11-21
        - Corrected timestamp conversion issues when monitoring a different timezone from the one the
          PC is located in
    v1.0.3 - 2018-11-09
        - Removed suntime and moontime exports, FormatTimeString() function
    v1.0.2 - 2018-11-02
        - Improved moon phase name function
        - Added debug logging description
    v1.0.1 - 2018-11-01
        - Fixed crash when moonrise is nil
    v1.0.0 - 2018-10-26
        - Initial release
    ----------------------------------------------------------------------------------------------------
]]--

debug = false -- set to true to enable debug logging
data = { moonViewAngle = 0, moonPhase = 0, moonPhaseName = 'New Moon' } -- set default values

function Initialize() end

function Update() end

-- generates all of the data and translates it into a format usable by the skin.
-- invoke through a !CommandMeasure bang.
-- all data table outputs that have to do with time are given as Windows FILETIME values.
function GenerateData(timestamp, latitude, longitude, tzOffset)

    -- setup timestamps
    local timestamp, mDate, zDate, ysDate, tmDate = SetupTimestamps(timestamp, tzOffset)

    -- retrieve data tables from SunCalc script
    sunTimes = SunCalc.getTimes(mDate, latitude, longitude)
    moonTimes = SunCalc.getMoonTimes(zDate, latitude, longitude)
    sunPosition = SunCalc.getPosition(mDate, latitude, longitude)
    moonPosition = SunCalc.getMoonPosition(mDate, latitude, longitude)
    moonIllumination = SunCalc.getMoonIllumination(mDate, latitude, longitude)
    -- debug logging
    RmLog('getTimes():')
    PrintTable(sunTimes)
    RmLog('getMoonTimes():')
    PrintTable(moonTimes)
    RmLog('getPosition():')
    PrintTable(sunPosition)
    RmLog('getMoonPosition():')
    PrintTable(moonPosition)
    RmLog('getMoonIllumination():')
    PrintTable(moonIllumination)

    -- fix moonrise / moonset times if necessary
    if moonTimes.rise ~= nil and (moonTimes.set == nil or moonTimes.set < moonTimes.rise and mDate > moonTimes.set) then
        -- set moonset to that of the next day
        moonTimes.set = SunCalc.getMoonTimes(tmDate, latitude, longitude)['set']
    elseif moonTimes.rise == nil or (moonTimes.set < moonTimes.rise and mDate < moonTimes.set) then
        -- set moonrise to that of the previous day
        moonTimes.rise = SunCalc.getMoonTimes(ysDate, latitude, longitude)['rise']
    end
    -- calculate suntime and moontime in minutes
    suntime = GetDifference(sunTimes.sunset, sunTimes.sunrise)
    moontime = GetDifference(moonTimes.set, moonTimes.rise)

    -- translate the data into the formats used by the skin
    data.sunrise = UnixToFiletime(CorrectTimestamp(sunTimes.sunrise), tzOffset)
    data.sunset = UnixToFiletime(CorrectTimestamp(sunTimes.sunset), tzOffset)
    data.moonrise = UnixToFiletime(CorrectTimestamp(moonTimes.rise), tzOffset)
    data.moonset = UnixToFiletime(CorrectTimestamp(moonTimes.set), tzOffset)
    data.moonViewAngle = math.deg(moonIllumination.angle - moonPosition.parallacticAngle)
    data.moonPhase = moonIllumination.phase
    data.moonPhaseName = GetMoonPhaseName(data.moonPhase)
    
    -- calculate sun and moon dial angles
    data.sunDialAngle = (GetDifference(mDate, sunTimes.sunrise) / suntime) * 180
    data.moonDialAngle = (GetDifference(mDate, moonTimes.rise) / moontime) * 180
    -- brute-force reset angle to -1 if things get wacky or if the object is set
    if data.sunDialAngle > 180 or data.sunDialAngle < 0 then data.sunDialAngle = -1 end
    if data.moonDialAngle > 180 or data.moonDialAngle < 0 then data.moonDialAngle = -1 end
    -- debug logging
    RmLog('data:')
    PrintTable(data)
    SKIN:Bang('!UpdateMeasureGroup', 'SunCalc')
    SKIN:Bang('!UpdateMeterGroup', 'SunCalc')
    SKIN:Bang('!Redraw')

end

-- converts a Windows FILETIME timestamp into a Unix epoch timestamp, accounting for timezone and DST, then returns several useful timestamps
function SetupTimestamps(timestamp, tzOffset)

    tzOffset = tzOffset or 0
    local localTz = (GetTimeOffset() / 3600)
    RmLog(localTz .. ' | ' .. tzOffset)
    tDate = os.date("!*t", timestamp)
    tDate.year = tDate.year - (1970 - 1601)
    timestamp = os.time(tDate) - (os.date('*t')['isdst'] and 3600 or 0)
    RmLog(timestamp)
    mDate = tonumber(tostring(timestamp) .. '000')   
    zDate = tonumber(tostring(os.time{ year = tDate.year, month = tDate.month, day = tDate.day, hour = 0, min = 0, sec = 0 }) .. '000')
    ysDate = zDate - 86400000  
    tmDate =  zDate + 86400000
    RmLog('zDate: ' .. zDate)

    return timestamp, -- current unix epoch timestamp value
           mDate,     -- 'millisecond date' (timestamp with three extra zeroes)
           zDate,     -- timestamp at current day, 0:00:00 (12:00 AM)
           ysDate,    -- timestamp at yesterday, 0:00:00 (12:00 AM)
           tmDate     -- timestamp at tomorrow, 0:00:00 (12:00 AM)

end

-- retrieves data from the data table using inline LUA in the skin
function GetData(key) return data[key] or '' end

-- ----- Utilities -----

moonPhases = {
    { 0.00, 0.03, 'New Moon'        },
    { 0.03, 0.23, 'Waxing Crescent' },
    { 0.23, 0.27, 'First Quarter'   },
    { 0.27, 0.48, 'Waxing Gibbous'  },
    { 0.48, 0.52, 'Full Moon'       },
    { 0.52, 0.73, 'Waning Gibbous'  },
    { 0.73, 0.77, 'Last Quarter'    },
    { 0.77, 0.98, 'Waning Crescent' },
    { 0.98, 1.01, 'New Moon'        }
}

function GetMoonPhaseName(phase)

    for i,v in pairs(moonPhases) do
        if phase >= v[1] and phase < v[2] then return v[3] end
    end

    return 'WTF?'

end

function UnixToFiletime(timestamp, tzOffset)

    local tDate = os.date("*t", timestamp)
    tDate.year = tDate.year + (1970 - 1601)
    tDate.hour = tDate.hour + tzOffset
    timestamp = os.time(tDate)
    return timestamp

end

function GetTimeOffset() return (os.time() - os.time(os.date('!*t')) + (os.date('*t')['isdst'] and 3600 or 0)) end

function GetDifference(a1, a2) return ((a1 - a2) / 60000) end

function CorrectTimestamp(timestamp) return tostring(timestamp):sub(1,10) end

-- function to make logging messages less cluttered
function RmLog(message, type)

    if type == nil then type = 'Debug' end
      
    if debug == true then
        SKIN:Bang("!Log", message, type)
    elseif type ~= 'Debug' then
        SKIN:Bang("!Log", message, type)
    end
      
end

printIndent = '     '

-- prints the entire contents of a table to the Rainmeter log
function PrintTable(table)
    for k,v in pairs(table) do
        if type(v) == 'table' then
            local pI = printIndent
            RmLog(printIndent .. tostring(k) .. ':')
            printIndent = printIndent .. '  '
            PrintTable(v)
            printIndent = pI
        else
            RmLog(printIndent .. tostring(k) .. ': ' .. tostring(v))
        end
    end
end

-- ------------------------------------------------------------------------------------------------------------------------
-- ------------------------------------------------------------------------------------------------------------------------
-- ------------------------------------------------------------------------------------------------------------------------

--[[
 (c) 2011-2015, Vladimir Agafonkin
 SunCalc is a JavaScript library for calculating sun/moon position and light phases.
 https://github.com/mourner/suncalc

 Copyright (c) 2014, Vladimir Agafonkin
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without modification, are
 permitted provided that the following conditions are met:
 
 1. Redistributions of source code must retain the above copyright notice, this list of
     conditions and the following disclaimer.
 
 2. Redistributions in binary form must reproduce the above copyright notice, this list
     of conditions and the following disclaimer in the documentation and/or other materials
     provided with the distribution.
 
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
]]--

-- shortcuts for easier to read formulas

PI   = math.pi
sin  = math.sin
cos  = math.cos
tan  = math.tan
asin = math.asin
atan = math.atan2
acos = math.acos
rad  = PI / 180

-- sun calculations are based on http://aa.quae.nl/en/reken/zonpositie.html formulas


-- date/time constants and conversions

dayMs = 1000 * 60 * 60 * 24
J1970 = 2440588
J2000 = 2451545

function toJulian(date)  return date / dayMs - 0.5 + J1970  end
function fromJulian(j)   return (j + 0.5 - J1970) * dayMs  end
function toDays(date)    return toJulian(date) - J2000  end


-- general calculations for position

e = rad * 23.4397 -- obliquity of the Earth 

function rightAscension(l, b)  return atan(sin(l) * cos(e) - tan(b) * sin(e), cos(l));  end
function declination(l, b)     return asin(sin(b) * cos(e) + cos(b) * sin(e) * sin(l));  end

function azimuth(H, phi, dec)   return atan(sin(H), cos(H) * sin(phi) - tan(dec) * cos(phi));  end
function altitude(H, phi, dec)  return asin(sin(phi) * sin(dec) + cos(phi) * cos(dec) * cos(H));  end

function siderealTime(d, lw)  return rad * (280.16 + 360.9856235 * d) - lw;  end

function astroRefraction(h)
    if (h < 0) then -- the following formula works for positive altitudes only. 
        h = 0 -- if h = -0.08901179 a div/0 would occur. 
    end

    -- formula 16.4 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. 
    -- 1.02 / tan(h + 10.26 / (h + 5.10)) h in degrees, result in arc minutes -> converted to rad: 
    return 0.0002967 / math.tan(h + 0.00312536 / (h + 0.08901179))

end

-- general sun calculations

function solarMeanAnomaly(d)  return rad * (357.5291 + 0.98560028 * d)  end

function eclipticLongitude(M)

    local C = rad * (1.9148 * sin(M) + 0.02 * sin(2 * M) + 0.0003 * sin(3 * M)) -- equation of center 
    local P = rad * 102.9372 -- perihelion of the Earth 

    return M + C + P + PI
end

function sunCoords(d)

    M = solarMeanAnomaly(d)
    L = eclipticLongitude(M)

    return {
        dec = declination(L, 0),
        ra = rightAscension(L, 0)
    }

end

SunCalc = {}

-- calculates sun position for a given date and latitude/longitude

SunCalc.getPosition = function (date, lat, lng)

    lw  = rad * -lng
    phi = rad * lat
    d   = toDays(date)
    c  = sunCoords(d)
    H  = siderealTime(d, lw) - c.ra

    return {
        azimuth = azimuth(H, phi, c.dec),
        altitude = altitude(H, phi, c.dec)
    }

end


-- sun times configuration (angle, morning name, evening name)

times = {
    {-0.833, 'sunrise',       'sunset'      },
    {  -0.3, 'sunriseEnd',    'sunsetStart' },
    {    -6, 'dawn',          'dusk'        },
    {   -12, 'nauticalDawn',  'nauticalDusk'},
    {   -18, 'nightEnd',      'night'       },
    {     6, 'goldenHourEnd', 'goldenHour'  }
}

-- adds a custom time to the times config

SunCalc.addTime = function (angle, riseName, setName)
    table.insert(times, {angle, riseName, setName})
end


-- calculations for sun times

J0 = 0.0009

function julianCycle(d, lw)  return math.round(d - J0 - lw / (2 * PI))  end

function approxTransit(Ht, lw, n)  return J0 + (Ht + lw) / (2 * PI) + n  end
function solarTransitJ(ds, M, L)   return J2000 + ds + 0.0053 * sin(M) - 0.0069 * sin(2 * L)  end

function hourAngle(h, phi, d)  return acos((sin(h) - sin(phi) * sin(d)) / (cos(phi) * cos(d)))  end

-- returns set time for the given sun altitude
function getSetJ(h, lw, phi, dec, n, M, L)

    w = hourAngle(h, phi, dec)
    a = approxTransit(w, lw, n)
    return solarTransitJ(a, M, L)

end


-- calculates sun times for a given date and latitude/longitude

SunCalc.getTimes = function (date, lat, lng)

    lw = rad * -lng
    phi = rad * lat

    d = toDays(date)
    n = julianCycle(d, lw)
    ds = approxTransit(0, lw, n)

    M = solarMeanAnomaly(ds)
    L = eclipticLongitude(M)
    dec = declination(L, 0)

    Jnoon = solarTransitJ(ds, M, L)

    i, len, time, Jset, Jrise = nil


    result = {
        solarNoon = fromJulian(Jnoon),
        nadir = fromJulian(Jnoon - 0.5)
    }

    for i = 1,table.length(times) do
        time = times[i]

        Jset = getSetJ(time[1] * rad, lw, phi, dec, n, M, L)
        Jrise = Jnoon - (Jset - Jnoon)

        result[time[2]] = fromJulian(Jrise)
        result[time[3]] = fromJulian(Jset)
    end

    return result

end


-- moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas

function moonCoords(d) -- geocentric ecliptic coordinates of the moon 

    L = rad * (218.316 + 13.176396 * d) -- ecliptic longitude 
    M = rad * (134.963 + 13.064993 * d) -- mean anomaly 
    F = rad * (93.272 + 13.229350 * d)  -- mean distance 

    l  = L + rad * 6.289 * sin(M) -- longitude 
    b  = rad * 5.128 * sin(F)    -- latitude 
    dt = 385001 - 20905 * cos(M)  -- distance to the moon in km 

    return {
        ra = rightAscension(l, b),
        dec = declination(l, b),
        dist = dt
    }

end

SunCalc.getMoonPosition = function (date, lat, lng)

    lw  = rad * -lng
    phi = rad * lat
    d   = toDays(date)

    c = moonCoords(d)
    H = siderealTime(d, lw) - c.ra
    h = altitude(H, phi, c.dec)
    -- formula 14.1 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998. 
    pa = atan(sin(H), tan(phi) * cos(c.dec) - sin(c.dec) * cos(H))

    h = h + astroRefraction(h) -- altitude correction for refraction 

    return {
        azimuth = azimuth(H, phi, c.dec),
        altitude = h,
        distance = c.dist,
        parallacticAngle = pa
    }

end


-- calculations for illumination parameters of the moon,
-- based on http://idlastro.gsfc.nasa.gov/ftp/pro/astro/mphase.pro formulas and
-- Chapter 48 of "Astronomical Algorithms" 2nd edition by Jean Meeus (Willmann-Bell, Richmond) 1998.

SunCalc.getMoonIllumination = function (date)

    d = toDays(date)
    s = sunCoords(d)
    m = moonCoords(d)

    sdist = 149598000 -- distance from Earth to Sun in km 

    phi = acos(sin(s.dec) * sin(m.dec) + cos(s.dec) * cos(m.dec) * cos(s.ra - m.ra))
    inc = atan(sdist * sin(phi), m.dist - sdist * cos(phi))
    angle = atan(cos(s.dec) * sin(s.ra - m.ra), sin(s.dec) * cos(m.dec) - cos(s.dec) * sin(m.dec) * cos(s.ra - m.ra))

    return {
        fraction = (1 + cos(inc)) / 2,
        phase = 0.5 + 0.5 * inc * (angle < 0 and -1 or 1) / math.pi,
        angle = angle
    }

end


function hoursLater(date, h)
    return date + h * dayMs / 24
end

-- calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article

SunCalc.getMoonTimes = function (date, lat, lng)

    t = date
    hc = 0.133 * rad
    h0 = SunCalc.getMoonPosition(t, lat, lng).altitude - hc
    h1, h2, rise, set, a, b, xe, ye, d, roots, x1, x2, dx = nil

    -- go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set) 
    for i=1,24,2 do
        h1 = SunCalc.getMoonPosition(hoursLater(t, i), lat, lng).altitude - hc
        h2 = SunCalc.getMoonPosition(hoursLater(t, i + 1), lat, lng).altitude - hc

        a = (h0 + h2) / 2 - h1
        b = (h2 - h0) / 2
        xe = -b / (2 * a)
        ye = (a * xe + b) * xe + h1
        d = b * b - 4 * a * h1
        roots = 0

        if d >= 0 then
            dx = math.sqrt(d) / (math.abs(a) * 2)
            x1 = xe - dx
            x2 = xe + dx
            if math.abs(x1) <= 1 then roots = roots + 1 end
            if math.abs(x2) <= 1 then roots = roots + 1 end
            if x1 < -1 then x1 = x2 end
        end

        if roots == 1 then
            if h0 < 0 then rise = i + x1
            else set = i + x1 end

        elseif roots == 2 then
            rise = i + (ye < 0 and x2 or x1)
            set = i + (ye < 0 and x1 or x2)
        end

        if rise and set then break end

        h0 = h2
    end

    result = {}

    if rise then result.rise = hoursLater(t, rise) end
    if set then result.set = hoursLater(t, set) end

    if not rise and not set then result[ye > 0 and 'alwaysUp' or 'alwaysDown'] = true end

    return result

end

-- ---------- NOT PART OF THE ORIGINAL SCRIPT - HAD TO BE ADDED FOR THE SCRIPT TO WORK IN LUA ----------

function math.round(x)
    if x%2 ~= 0.5 then
        return math.floor(x+0.5)
    end
    return x-0.5
end

function table.length(T)
    local count = 0
    for _ in pairs(T) do count = count + 1 end
    return count
end
”We are pretty sure that r2922 resolves the regression in resolution caused by a reversion to a revision.” - jsmorley, 2017
User avatar
balala
Rainmeter Sage
Posts: 8288
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: [Lots of Math] Moon Icon Rotation on a Sky Dial

balala » December 31st, 2018, 9:06 pm

Please pack the whole config (with all needed files - like the .lua - and resources) and upload it.
User avatar
raiguard
Posts: 630
Joined: June 25th, 2015, 7:02 pm
Location: The Sky, USA

Re: [Lots of Math] Moon Icon Rotation on a Sky Dial

raiguard » December 31st, 2018, 9:12 pm

balala wrote:
December 31st, 2018, 9:06 pm
Please pack the whole config (with all needed files - like the .lua - and resources) and upload it.
I actually removed tons of irrelevant code from the skin and merged it all into one file to simplify debugging the moon icon. The skin right now doesn't use SunCalc, and doesn't require any external files to work. Would you still like me to package the original?
”We are pretty sure that r2922 resolves the regression in resolution caused by a reversion to a revision.” - jsmorley, 2017
User avatar
balala
Rainmeter Sage
Posts: 8288
Joined: October 11th, 2010, 6:27 pm
Location: Gheorgheni, Romania

Re: [Lots of Math] Moon Icon Rotation on a Sky Dial

balala » December 31st, 2018, 9:17 pm

raiguard wrote:
December 31st, 2018, 9:12 pm
I actually removed tons of irrelevant code from the skin and merged it all into one file to simplify debugging the moon icon. The skin right now doesn't use SunCalc, and doesn't require any external files to work. Would you still like me to package the original?
You've posted the content of a .lua file, that's why I asked to upload it. How does the latest code look like? If you've changed the lastly posted code, please post / upload it.