Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WPF

WPF 3D Dome Creator

4.96/5 (13 votes)
14 Jan 2011CPOL4 min read 72K   8.7K  
WPF 3D Dome Creator using MVVM
screenshot1.png

Introduction

I am an adult fan of LEGO and while working on my recent LEGO project, I had to build a dome made of LEGO bricks. After trying a couple of LEGO CAD applications, I still was having difficulty building the dome. Then I stumbled upon a LEGO dome creator application from a guy called Arthur Gugick. This application was developed using VB6 and it had some bugs like the form would be cleared once it was minimised. This application gives the height of the dome and every stud location and this is also how WPF 3D Dome Creator functions. However it was all 2D, one could not visualise how the dome will look like. So I contacted Arthur and asked him if it would be possible to send the source code. He was more than happy to provide it and I took that as a base for the WPF 3D Dome Creator.

About LDrawPartLib

LDrawPartLib was developed by me for LEGO CAD application similar to MLCad, which is still work in progress but the library is complete. LDrawPartLib uses the LDraw part library for the part geometry. LDraw is an open source standard of LEGO CAD application. They have a very active community and regularly update the library with new parts. I feel that discussing about LDrawPartLib here is beyond the scope of this article. However, if you are interested in how the LDrawPartLib parses and creates 3D LEGO part, you will have to go through the code, which had decent amount of comments and the documentation from LDraw. You can of course contact me anytime if you have something very specific.

About the Dome Creator

I have used this application as a way to learn more about WPF, 3D and the famed MVVM. This application is understanding for MVVM and WPF implementation should be done. I am open to your comments and ways to improve this application. Since this application uses only one part, all the required files are places in the “Parts” folder. If you want to use the LDraw parts library, you will need to download it from here and the place the path in the App.Config. Also ldconfig.ldr, which a part of LDraw, needs to placed in the application path as it has all the details about the part colors.

The application itself is very simple to use. Choose the type of dome that you would like to build, set the various parameters like the diameter and the height of the dome, the height is consider only when the dome type is not spherical. You can change the color of the dome by clicking on the color palette.

Points of Interest

First and foremost, it uses WPF 3D. I have used WPF 3D as I wanted to know what are its capabilities. It is a great 3D framework, however I seriously doubt that this will be used for developing games. There are many drawbacks, for one the framework does not come with built in support for creating basic figures like cubes, spheres, etc., secondly, as I learnt the hard way that drawing line on a 3D canvas can be a real pain. However, I later found out about how to draw lines by using 3DTools and a library from Charles Petzold. He has also written an excellent book on WPF 3D named 3D Programming for Windows. The former has a bug in the implementation, more on this here, and so I did not to use the line implementation of 3DTools. However, the TrackBall implementation of 3DTools is fantastic. The second one has some excellent features but the performance goes down considerably as the number parts with lines increases in my 3D scene. If you want to see the parts with lines, uncomment the following line:

C#
public class Dome3DViewModel : DomeViewModel
{
	...
	public void CalculateDomeValues() {
		...
		int y = (currentHeight - noOfPlates) * (int)ScaleHeight;
		for (int i = 0; i < noOfPlates; i++, y += (int)ScaleHeight) {

			Point3D pt = new Point3D(x * ScaleWidth, y, z * ScaleWidth);

			PartColors color = 
                (PartColors)Enum.Parse(typeof(PartColors), 
                            MainWindowVM.ColorChooserVM.CurrentColor.Name);

			Part3D part = 
                new Part3D( PLATE_PART_CODE, 
                            PLATE_PART_CODE, color) { Position = pt };
			//part.ShowLines = true;

			Viewport.Children.Add(part);
		}
		...
	}
}

There is an excellent tutorial on WPF 3D here.

Secondly, as I mentioned before, the program by Arthur gives the height in LEGO plates at a particular location. I could have inserted the exact number of plates from the “ground” but this however would create a solid dome. Hence, I decided to add only “viewable” plates. This is achieved by finding which of its eight neighbours in the grid have the lowest height and then add only the difference between the lowest and the current height. In order to achieve this, I used the following:

C#
List<int> neighbours = new List<int>();
...
// Fill all the values from the surrounding neighbours.
FillNeighbours(diameter, z, drCurrent, neighbours);
if (drPrev != null) FillNeighbours(dt.Rows.Count, z, drPrev, neighbours);
if (drNext != null) FillNeighbours(dt.Rows.Count, z, drNext, neighbours);

// Gets the lowest values on the top.
neighbours.Sort();

// no of viewable plates.
if (drNext == null || drPrev == null || z == 0 || z == (diameter - 1))
    noOfPlates = currentHeight; // if dome edges then show all the plates.
else if (currentHeight == neighbours[0] && currentHeight > 0)
    noOfPlates = 1; 	// If lowest neighbours is of the current height 
			// then add only one plate.
else // no need the show all the plate. Just show the 'viewable' plates.
    noOfPlates = currentHeight - neighbours[0];

Thirdly, I wanted to have access the one view model from other. To achieve this, I created a base class called ApplicationViewModel.

C#
public abstract class ApplicationViewModel : ViewModelBase
{
	#region ctor
	protected ApplicationViewModel(MainWindowViewModel mainWindowModel) {
		MainWindowVM = mainWindowModel;
	}
	#endregion

	#region Public Properties
	public MainWindowViewModel MainWindowVM { get; set; }
	#endregion
}

The constructor of this class takes MainWindowViewModel as a parameter. All the view models that need to talk to each other in the application derive from ApplicationViewModel.

C#
public class ParametersViewModel : ApplicationViewModel
{
	#region ctor

	public ParametersViewModel(MainWindowViewModel mainWindowModel) 
            : base(mainWindowModel) {
		DomeDiameter = 12;
		DomeHeight = 16;
	}
	#endregion
	...
} 

The MainWindowViewModel has properties for each of these models.

C#
public class MainWindowViewModel : ViewModelBase
{
	#region ctor
	public MainWindowViewModel() {
		ParametersVM = new ParametersViewModel(this);
		Dome2DVM = new Dome2DViewModel(this);
		Dome3DVM = new Dome3DViewModel(this);
		ColorChooserVM = new ColorChooserViewModel(this);
	}
	#endregion

	#region Public Properties
	public ParametersViewModel ParametersVM { get; set; }
	public Dome2DViewModel Dome2DVM { get; set; }
	public Dome3DViewModel Dome3DVM { get; set; }
	public ColorChooserViewModel ColorChooserVM { get; set; }
	#endregion
}

Conclusion

As a learner, I would love to know what you think about the way in which this application is developed. It would be great if the criticism is constructive.

Lastly, LEGO is a registered trademark of the LEGO Group, which does not sponsor, endorse, or authorize this article or this application. Visit the official Lego website at www.lego.com.

History

  • 14th January, 2011: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)