Discount week - May Swift week
Save up to 80 % on our Swift e-learning courses. Only this week!
Get up to 60 % extra points for free! More info

Planet Trajectories

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

Problem

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?

Solution

I realized that drawing such curve using Bezier looks rather complicated. Then I found that the Windows Forms libraries already provide this feature, specifically, the Graphics.DrawCurve(Pen pen, Point[] points); method. So I decided to port the application from WPF to WinForms.

Scrollbar controls width adjustment

Problem

We have a FlowLayoutPanel with FlowDirection = TopDown. We want a ScrollBar to appear when there isn't enough space for the controls, so we set the AutoScroll to True and the WrapContents to False. 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.

Solution

The solution is to use a classic panel, instead of the FlowLayoutPanel. The panel's AutoScroll is set to True, and the controls inside set their Dock to 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

Problem

The planets have the Parent property, which specifies which planet the planet is orbiting. At first, I was selecting the planet in a ComboBox (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 the 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 very messy.

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.

Solution

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 (MouseEnter and MouseWheel events).

DropDown empty item

Problem

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 null value. 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 change.

Solution

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 we want.

Automatic scale, centering and zooming

Problem

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.

Solution

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.

Picture fonts

Problem

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.

Solution

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 :P ) 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

Problem

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.

Solution

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 paths).

Conclusion

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.


Gallery

Program was created in 2015.

 

Download

Downloaded 0x (458.72 kB)
Application includes source codes in language C# .NET

 

 

Program has been written for you by David Dostal
Avatar
Do you like this article?
No one has rated this quite yet, be the first one!
All articles in this section
C# .NET Community Projects - Windows Forms Applications
Activities (1)

 

 

Comments

To maintain the quality of discussion, we only allow registered members to comment. Sign in. If you're new, Sign up, it's free.

No one has commented yet - be the first!