Pascal's Triangle - C# GDI Display

AceInfinity

Emeritus, Contributor
Joined
Feb 21, 2012
Posts
1,728
Location
Canada
Images:
kDf0yRX.png


Video:
edit: New version (April 19, 2013)

Source:

*Code from old version - new version on CodePlex.
PascalsTriangle.cs
Code:
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections.Generic;
using System.Windows.Forms;

namespace PascalsTriangleDisplay.UI
{
	sealed class PascalsTriangle : PictureBox
	{
		public PascalsTriangle()
		{
			DoubleBuffered = true;
			Size = new Size(100, 100);
			SetStyle(ControlStyles.SupportsTransparentBackColor, true);
			BackColor = Color.Transparent;
		}

		public int Base { get; set; }

		private int _rad = 2;
		public int Rad
		{
			get { return _rad; }
			set { _rad = value; }
		}

		public Color DefaultBaseColor { get; set; }
		public Dictionary<int, Color> BaseColors { get; set; }

		private const decimal Sqrt2 = 1.4142135623730950488016887242m;

		protected override void OnPaint(PaintEventArgs e)
		{
			base.OnPaint(e);
			e.Graphics.SmoothingMode = SmoothingMode.HighQuality;

			int gridTiles = Width / Rad;
			decimal dy = Sqrt2 * Rad;

			int[] previousColors = new int[] { };
			for (int i = 0; i < gridTiles / 2 - Rad; i++)
			{
				int[] rowColors = new int[i + 1];
				for (int j = 0; j <= i; j++)
				{
					int colorIndex;
					if (j == 0 || j == i)
					{
						colorIndex = 1;
					}
					else
					{
						int leftColor = previousColors[j - 1];
						int rightColor = previousColors[j];
						colorIndex = (leftColor + rightColor) % Base;
					}

					Color fillColor = BaseColors.ContainsKey(colorIndex) ? BaseColors[colorIndex] : DefaultBaseColor;

					DrawElipse(e.Graphics, fillColor, new Point((Width / 2) + Rad * (2 * j - i), (int)(dy * i * 1.415m)), Rad * 2);
					rowColors[j] = colorIndex;
				}
				previousColors = rowColors;
			}
		}

		private int _prevSize;
		protected override void OnResize(EventArgs e)
		{
			base.OnResize(e);
			if (Height != _prevSize)
			{
				_prevSize = Width = Height;
			}
			else
			{
				_prevSize = Height = Width;
			}
			Invalidate();
		}

		private static void DrawElipse(Graphics g, Color fill, Point location, int diameter)
		{
			Size s = new Size(diameter, diameter);
			g.DrawEllipse(Pens.Black, new Rectangle(location, s));

			using (SolidBrush sb = new SolidBrush(Color.FromArgb(200, fill)))
				g.FillEllipse(sb, new Rectangle(location, s));
		}
	}
}

Form1.cs
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;
using System.Windows.Forms;

namespace PascalsTriangleDisplay
{
	public sealed partial class Form1 : Form
	{
		private int _colorCount;
		private readonly BindingList<Color> _baseColors = new BindingList<Color>();

		// Constructor
		public Form1()
		{
			InitializeComponent();
			DoubleBuffered = true;
			SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
			_baseColors.ListChanged += _baseColors_ListChanged;
			nudBase.Value = 3;
			nudRad.Value = 3;
		}

		// Base Colors Changed
		void _baseColors_ListChanged(object sender, ListChangedEventArgs e)
		{
			colorListView1.Items.Clear();
			Dictionary<int, Color> colors = new Dictionary<int, Color>();
			colorListView1.BeginUpdate();
			for (int i = 0; i < _baseColors.Count; i++)
			{
				Color baseColor = _baseColors[i];
				colors.Add(i, baseColor);
				ListViewItem lvi = new ListViewItem(new[] { i.ToString(CultureInfo.InvariantCulture), string.Format("RGB: {0}, {1}, {2}", baseColor.R, baseColor.G, baseColor.B) });
				lvi.SubItems[1].BackColor = baseColor;
				colorListView1.Items.Add(lvi);
			}
			colorListView1.EndUpdate();
			pascalsTriangle1.BaseColors = colors;
			_colorCount = pascalsTriangle1.Base;
		}

		// Update
		private void btnUpdate_Click(object sender, EventArgs e)
		{
			pascalsTriangle1.Invalidate();
		}

		// Value Changes
		private void nudBase_ValueChanged(object sender, EventArgs e)
		{
			pascalsTriangle1.Base = (int)nudBase.Value;
			if (_colorCount < pascalsTriangle1.Base)
			{
				for (int i = _colorCount; i < pascalsTriangle1.Base; i++)
				{
					_baseColors.Add(Color.Black);
				}
			}
			else
			{
				for (int i = 0; i < _colorCount - pascalsTriangle1.Base; i++)
				{
					_baseColors.RemoveAt(_baseColors.Count - 1);
				}
			}
		}

		private void nudRad_ValueChanged(object sender, EventArgs e)
		{
			pascalsTriangle1.Rad = (int)nudRad.Value;
		}

		// Update Base Color
		private void colorListView1_DoubleClick(object sender, EventArgs e)
		{
			if (colorListView1.SelectedItems.Count > 0)
			{
				using (ColorDialog cd = new ColorDialog())
				{
					cd.AnyColor = true;
					cd.FullOpen = true;
					if (cd.ShowDialog() == DialogResult.OK)
					{
						_baseColors[colorListView1.SelectedIndices[0]] = cd.Color;
					}
				}
			}
		}

	}
}

New version: https://pascalstriangle.codeplex.com/

Enjoy
:thumbsup2:
 
Last edited:
Haha, this is awesome, I've added transparency support and the capability to save the image:
AMDta9x.png


Result:
JAUEdAj.png


edit: Who wants a desktop background? lol
wM5FePw.png


Having fun with this one. :thumbsup2:

edit: Latest preview, I created my own color picker control and I put the image inside of a Window for easier manipulation:
6aG7BHh.png


Now it doesn't depend on the size of my monitor, I can create any size I want, provided that the CPU and RAM can handle it... I created a 5000x5000 triangle just a few minutes ago. :)
 
Last edited:
It may not get much faster as that's a ton of rendering and drawing. I didn't cap it off though, so for as far as the Maximum value goes in that control, is what you can use as an image size. It just depends on how risky you want to be and how long you want to wait. Who needs an image that size though unless you have an IMAX theater to display it in? :grin1:
 
Last edited:
lol, good point. Any idea why Windows wouldn't like displaying the image? It seems to vary between the two attached screenshots :confused2: Actually, saying that, are the screenshots actually pascal's triangles? They look odd.
 

Attachments

  • 1.PNG
    1.PNG
    256 KB · Views: 2
  • 2.PNG
    2.PNG
    328.7 KB · Views: 0
Well, I've always called them Sierpinski's Triangles, but that doesn't mean they can't be generated in some way from Pascal's Triangles???
 
I can give it a shot. :)

@tom - I'm not sure what you mean by the images in this post: https://www.sysnative.com/forums/programming/5853-pascals-triangle-c-gdi-display.html#post45394

Good luck!

I've made a triangle and saved it as a PNG with your app but when I open it with Windows Photo Viewer, sometimes it will have a grey background and others not, in a seemingly random order. Those two attachments are screenshots of me opening the same image :)

Did you choose to opt for Transparency by ticking the checkbox? Also, the transparency color needs to be set to the exact color that you want to be transparent in order for it to work. And if you don't click the "Update" button, you will be saving possibly non-updated views of the triangle.
 
That's different. I've never encountered that before. The transparency in the image is fine, but perhaps just a modified display that the Windows PhotoViewer program uses for helping you see images that have mostly transparent portions in the image? You didn't change anything in the default transparency base color, so everything from the first few bases being colored, is all transparent. It's probably a feature of the PhotoViewer program itself though, and not an issue with the transparent PNG.
 

Has Sysnative Forums helped you? Please consider donating to help us support the site!

Back
Top