What is the purpose of the application?
The program calculates and draws planet trajectories (their relative distance to the selected "center" planet). The selected planets and settings can be saved into a file and then loaded from it. The program also supports exporting the image in .png or .bmp format.
Why planet trajectories?
During astronomy lessons in physics, we had to draw these trajectories manually (it took several hours). As my friend suggested to me, I started working on a program to ease the work. I came up with the position finding algorithm already during the lessons. It's actually not so complicated, but I had no idea that the application with all the stuff around (application design, my wished GUI, correct rendering, program flexibility, saving into file etc.) will grow into my largest project so far and will take dozens of hours to make it.
Note: For simplicity, any object orbiting another object is refered as the planet.
What have I learned?
I'd like to share some of my experiences I've gained during the development, and I hope they'll help solve problems similar to those I've encountered during the development.
Points connecting curve
How to connect planet trajectory points with a smooth curve? I didn't want to connect points with straight lines - it wouldn't look good, and for any slightly smoother curve it'd be necessary to generate many points. So what to do?
I realized that drawing such curve using Bezier looks rather complicated.
Then I found that the Windows Forms libraries already provide this feature,
Graphics.DrawCurve(Pen pen, Point points);
method. So I decided to port the application from WPF to WinForms.
Scrollbar controls width adjustment
We have a
FlowDirection = TopDown. We want a ScrollBar to appear when there
isn't enough space for the controls, so we set the
True and the
problem is that the ScrollBar appears as expected, but it takes some space in
the panel. Therefore, the controls don't fit within the panel's width, and the
horizontal ScrollBar also appears. This isn't the desired behavior. It'd be
better if the controls shrinked a bit horizontally to make some space for the
ScrollBar. But I haven't figured out how to achieve it without using my own code
in the background, but then I came up with another solution.
The solution is to use a classic panel, instead of the FlowLayoutPanel. The
AutoScroll is set to
True, and the controls
inside set their
Top. Simple, right? If the
controls don't fit in, the vertical ScrollBar appears and the controls shrink a
bit to make some space for it.
Hiding DropDown items
The planets have the
Parent property, which specifies which
planet the planet is orbiting. At first, I was selecting the planet in a
DropDownStyle = DropDownList), which was binded to the
planets list. The problem was that I couldn't set the planet itself or any of
its "children" as the parent, because these mustn't appear in the ComboBox. The
question is how to hide them inside the ComboBox. The first attempts were using
Binding.Format event, but without any success. The planets
would be either removed from the source BindingList (which is what we, of
course, don't want), or the binding wouldn't work because we'd copy the items
into a new collection. Another option I found was to hide the unwanted items
inside the ComboBox, but that had several issues. First, the ComboBox itself
would have to be rewritten to hide the unwanted items. This includes creating a
custom control and implementing its rendering and positioning, which might
create new problems. Second, users are always capable of finding a way to break
something. For example, if we used keyboard keys to control the form, we could
also use them to select the hidden items (which are still present, just not
visible). Third, it'd be necessary to modify the program to somehow update the
visibility information, which would interfere with the existing code and make it
I've been looking for any working solution for a long time, but I haven't found anything functional. In the end, I've decided to try to find different way to select the parent planet. I came up with a dialog containing the ComboBox, which would appear after clicking a button. The ComboBox will then display the planets that can be used as the "parent". Then I realized something - there was no need to create a new dialog which contains only the original ComboBox.
The solution is to update the ComboBox in the
Enter event that
is triggered whenever the respective control becomes the active control of the
form. Here we simply get the valid items (only a single line of code when using
LINQ) and display them to the user. DropDown is updated whenever the user wants
to select a new item, using either the mouse or the keyboard.
Appendix: I forgot something anyway. When the user hoveres over the ComboBox
and scrolls, the selected item can be changed without triggering the Enter
event. You can see the final solution it the source code of the project
DropDown empty item
When a planet doesn't orbit another planet (such as the Sun), its
Parent property is set to
null. So it'd be a good idea
to add an empty item into the ComboBox to represent the
But we can't add the
null value into a collection or DropDown
because all empty places in the list are already null and nothing would
The solution is to add an item with the "<None>" text in the method
that filters the appropriate planets (it can also be an empty string). When
casting the selected item in the
SelectedValueChanged event back to
its original type (in this case, the
Planet), instead of
(Planet) ComboBox.SelectedItem, it's necessary to write
ComboBox.SelectedItem as Planet. So casting a string to the planet
won't throw us an exception, but only make the value
null - just as
Automatic scale, centering and zooming
The distances between planets can be very different, and with the same scale we can sometimes see a single planet across the whole screen, and sometimes the entire solar system is just a dot. It'd be good to automatically adjust the scale.
Winforms will greatly ease this work with its built-in features. First we
translate the [0; 0] point to the center:
g.TranslateTransform(.VisibleClipBounds.Width / 2, g.VisibleClipBounds.Height / 2).
To change the scale, we simply pass the scale in both axes to the
Graphics.ScaleTransform(float sx, float sy) method. Each rendered
point is then automatically translated. Now we have to find the proper scale.
This can be obtained as follows:
zoom * canvas size / rendered image max size.
Originally, I used image fonts such as Wingdings3 for the icons. However, I learned that on some devices the font isn't installed, and empty rectangles are displayed instead.
Although fonts can be imported in a slightly more complicated way using the system libraries, there still might be problems with copyrights. Making (even prettier ) icons in Inkscape and setting them as the background image of the controls was a matter of minutes, while having less work and wondering about nonsense copyright issues.
Files opening from the application
It'd be terrific if the application supported files opening function using the "Open with..." option or by dragging the file to the application icon.
The solution is very simple. I didn't need any help from the Internet, and it
was exactly as I thought. In the
Program.cs file, add the
string args argument to the
static void Main()
method (as in a console application). This will get the arguments with which the
application was launched (in this case, the array will contain individual file
I'd like to continue developing the application, but I don't know whether I'll have time for it. With all the guesswork, I’ve spent slightly less than 100 hours developing it, so I’ll be happy for your support (even a nice comment that the app works well ). Future releases may come up with, for example, a ProgressBar that indicates the rendering status.
Even though the application didn't fulfill its original purpose of saving work, I'm still very glad that I made it. The most precious thing about this is the experience I've gained.
As always, I'm looking forward to any comments, bug reports, and discussions about other possible solutions that come to your mind.
DownloadBy downloading the following file, you agree to the license terms
Downloaded 2x (458.72 kB)
Application includes source codes in language C# .NET
No one has commented yet - be the first!