Click here to Skip to main content
15,892,005 members
Articles / Programming Languages / C#

C#: A-Star is born

Rate me:
Please Sign up or sign in to vote.
4.92/5 (185 votes)
23 Jun 20037 min read 572.4K   8.6K   362  
Understand graphs and A* path-finding algorithm with C#
// Copyright 2003 Eric Marchesin - <eric.marchesin@laposte.net>
//
// This source file(s) may be redistributed by any means PROVIDING they
// are not sold for profit without the authors expressed written consent,
// and providing that this notice and the authors name and all copyright
// notices remain intact.
// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED. USE IT AT YOUR OWN RISK. THE AUTHOR ACCEPTS NO
// LIABILITY FOR ANY DATA DAMAGE/LOSS THAT THIS PRODUCT MAY CAUSE.
//-----------------------------------------------------------------------
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using EMK.Cartography;


namespace GraphFormer
{
	/// <summary>
	/// Summary description for Form1.
	/// </summary>
	public class GraphFormer : System.Windows.Forms.Form
	{
		#region Construction / Destruction

		private ContextMenu MenuContextuel;
		APropos DialogueAPropos = new APropos();
		private FlickerFreePanel GraphPanel;

		private System.Windows.Forms.ToolBarButton AEtoileEtape;
		private System.Windows.Forms.ImageList ImagesActions;
		private System.Windows.Forms.ImageList ImagesPasAPas;
		private System.Windows.Forms.ToolBar FichierToolBar;
		private System.Windows.Forms.ImageList ImagesFichier;
		private System.Windows.Forms.ToolBarButton BoutonSauver;
		private System.Windows.Forms.ToolBarButton BoutonCharger;
		private System.Windows.Forms.ToolBarButton Sep1;
		private System.Windows.Forms.ToolBarButton Sep2;
		private System.Windows.Forms.Label LabelAide;
		private System.Windows.Forms.ToolBarButton BoutonNouveau;
		private System.Windows.Forms.ToolBarButton BoutonDessiner;
		private System.Windows.Forms.ToolBarButton BoutonEffacer;
		private System.Windows.Forms.ToolBarButton BoutonDeplacer;
		private System.Windows.Forms.ToolBarButton BoutonChangerEtat;
		private System.Windows.Forms.ToolBarButton BoutonAEtoile;
		private System.Windows.Forms.ToolBarButton BoutonAProposDe;
		private System.Windows.Forms.StatusBar GraphStatusBar;
		private System.Windows.Forms.StatusBarPanel NbNodesPanel;
		private System.Windows.Forms.StatusBarPanel NbArcsPanel;
		private System.Windows.Forms.StatusBarPanel CoordsPanel;
		private System.Windows.Forms.ToolBar EditionToolBar;
		private System.Windows.Forms.ToolBar AEtoileToolBar;
		private System.Windows.Forms.ToolBarButton AEtoileDebut;
		private System.Windows.Forms.ToolBarButton AEtoileFin;
		private System.ComponentModel.IContainer components;

		static int Rayon = 7;
		static int Epaisseur = 1;
		static Pen CrayonNoeuds;
		static Pen CrayonArcs;
		static Pen CrayonNoeudsInactifs;
		static Pen CrayonArcsInactifs;
		static Pen CrayonTemp;
		static Pen CrayonChemin;
		static Pen CrayonArcsPas;

		static GraphFormer()
		{
			CrayonNoeuds = new Pen(Color.Black, Epaisseur);
			CrayonNoeudsInactifs = new Pen(Color.Gray, Epaisseur);

			CrayonArcs = new Pen(Color.Black, Epaisseur);
			CrayonArcs.EndCap = LineCap.Custom;
			CrayonArcs.CustomEndCap = new AdjustableArrowCap(3, 6, true);
			CrayonArcsInactifs = new Pen(Color.Gray, Epaisseur);
			CrayonArcsInactifs.EndCap = LineCap.Custom;
			CrayonArcsInactifs.CustomEndCap = new AdjustableArrowCap(3, 6, true);

			CrayonTemp = new Pen(Color.Gray, Epaisseur);
			CrayonTemp.DashStyle = DashStyle.Dash;

			CrayonChemin = new Pen(Color.DarkTurquoise, 3);
			CrayonChemin.DashStyle = DashStyle.Dot;

			CrayonArcsPas = new Pen(Color.LawnGreen, 3);
			CrayonArcsPas.EndCap = LineCap.Custom;
			CrayonArcsPas.CustomEndCap = new AdjustableArrowCap(4, 8, true);
		}

		public GraphFormer()
		{
			MenuContextuel = new ContextMenu();
			MenuContextuel.MenuItems.Add(new MenuItem("Automatic", new EventHandler(ChoixAutomatique)));
			MenuContextuel.MenuItems.Add(new MenuItem("Step by step", new EventHandler(ChoixPasAPas)));
			InitializeComponent();
			BoutonAEtoile.DropDownMenu = MenuContextuel;

			G = new Graph();
			NouveauGraphe();
			Mode = Action.Dessiner;
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing && components != null) components.Dispose();
			base.Dispose( disposing );
		}
		#endregion

		#region Windows Form Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			this.components = new System.ComponentModel.Container();
			System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(GraphFormer));
			this.GraphPanel = new FlickerFreePanel();
			this.EditionToolBar = new System.Windows.Forms.ToolBar();
			this.BoutonDessiner = new System.Windows.Forms.ToolBarButton();
			this.BoutonEffacer = new System.Windows.Forms.ToolBarButton();
			this.BoutonDeplacer = new System.Windows.Forms.ToolBarButton();
			this.BoutonChangerEtat = new System.Windows.Forms.ToolBarButton();
			this.BoutonAEtoile = new System.Windows.Forms.ToolBarButton();
			this.ImagesActions = new System.Windows.Forms.ImageList(this.components);
			this.GraphStatusBar = new System.Windows.Forms.StatusBar();
			this.NbNodesPanel = new System.Windows.Forms.StatusBarPanel();
			this.NbArcsPanel = new System.Windows.Forms.StatusBarPanel();
			this.CoordsPanel = new System.Windows.Forms.StatusBarPanel();
			this.FichierToolBar = new System.Windows.Forms.ToolBar();
			this.BoutonNouveau = new System.Windows.Forms.ToolBarButton();
			this.BoutonCharger = new System.Windows.Forms.ToolBarButton();
			this.BoutonSauver = new System.Windows.Forms.ToolBarButton();
			this.BoutonAProposDe = new System.Windows.Forms.ToolBarButton();
			this.Sep1 = new System.Windows.Forms.ToolBarButton();
			this.ImagesFichier = new System.Windows.Forms.ImageList(this.components);
			this.AEtoileToolBar = new System.Windows.Forms.ToolBar();
			this.Sep2 = new System.Windows.Forms.ToolBarButton();
			this.AEtoileDebut = new System.Windows.Forms.ToolBarButton();
			this.AEtoileEtape = new System.Windows.Forms.ToolBarButton();
			this.AEtoileFin = new System.Windows.Forms.ToolBarButton();
			this.ImagesPasAPas = new System.Windows.Forms.ImageList(this.components);
			this.LabelAide = new System.Windows.Forms.Label();
			((System.ComponentModel.ISupportInitialize)(this.NbNodesPanel)).BeginInit();
			((System.ComponentModel.ISupportInitialize)(this.NbArcsPanel)).BeginInit();
			((System.ComponentModel.ISupportInitialize)(this.CoordsPanel)).BeginInit();
			this.SuspendLayout();
			// 
			// EditionToolBar
			// 
			this.EditionToolBar.Appearance = System.Windows.Forms.ToolBarAppearance.Flat;
			this.EditionToolBar.Buttons.AddRange(new System.Windows.Forms.ToolBarButton[] {
																							  this.BoutonDessiner,
																							  this.BoutonEffacer,
																							  this.BoutonDeplacer,
																							  this.BoutonChangerEtat,
																							  this.BoutonAEtoile});
			this.EditionToolBar.ButtonSize = new System.Drawing.Size(16, 16);
			this.EditionToolBar.Cursor = System.Windows.Forms.Cursors.Hand;
			this.EditionToolBar.Divider = false;
			this.EditionToolBar.Dock = System.Windows.Forms.DockStyle.None;
			this.EditionToolBar.DropDownArrows = true;
			this.EditionToolBar.ImageList = this.ImagesActions;
			this.EditionToolBar.Location = new System.Drawing.Point(104, 0);
			this.EditionToolBar.Name = "EditionToolBar";
			this.EditionToolBar.ShowToolTips = true;
			this.EditionToolBar.Size = new System.Drawing.Size(136, 23);
			this.EditionToolBar.TabIndex = 0;
			this.EditionToolBar.ButtonClick += new System.Windows.Forms.ToolBarButtonClickEventHandler(this.GraphToolBar_ButtonClick);
			// 
			// BoutonDessiner
			// 
			this.BoutonDessiner.ImageIndex = 0;
			this.BoutonDessiner.Style = System.Windows.Forms.ToolBarButtonStyle.ToggleButton;
			this.BoutonDessiner.Tag = 0;
			this.BoutonDessiner.ToolTipText = "Draw nodes and arcs";
			// 
			// BoutonEffacer
			// 
			this.BoutonEffacer.ImageIndex = 1;
			this.BoutonEffacer.Style = System.Windows.Forms.ToolBarButtonStyle.ToggleButton;
			this.BoutonEffacer.Tag = 1;
			this.BoutonEffacer.ToolTipText = "Erase nodes and arcs";
			// 
			// BoutonDeplacer
			// 
			this.BoutonDeplacer.ImageIndex = 2;
			this.BoutonDeplacer.Style = System.Windows.Forms.ToolBarButtonStyle.ToggleButton;
			this.BoutonDeplacer.Tag = 2;
			this.BoutonDeplacer.ToolTipText = "Move nodes";
			// 
			// BoutonChangerEtat
			// 
			this.BoutonChangerEtat.ImageIndex = 3;
			this.BoutonChangerEtat.Style = System.Windows.Forms.ToolBarButtonStyle.ToggleButton;
			this.BoutonChangerEtat.Tag = 3;
			this.BoutonChangerEtat.ToolTipText = "Change the state of nodes and arcs";
			// 
			// BoutonAEtoile
			// 
			this.BoutonAEtoile.ImageIndex = 4;
			this.BoutonAEtoile.Style = System.Windows.Forms.ToolBarButtonStyle.DropDownButton;
			this.BoutonAEtoile.Tag = 4;
			this.BoutonAEtoile.ToolTipText = "Place starting and ending flags, then find the best path.";
			// 
			// ImagesActions
			// 
			this.ImagesActions.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
			this.ImagesActions.ImageSize = new System.Drawing.Size(16, 16);
			this.ImagesActions.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("ImagesActions.ImageStream")));
			this.ImagesActions.TransparentColor = System.Drawing.Color.Transparent;
			// 
			// GraphStatusBar
			// 
			this.GraphStatusBar.Location = new System.Drawing.Point(0, 494);
			this.GraphStatusBar.Name = "GraphStatusBar";
			this.GraphStatusBar.Panels.AddRange(new System.Windows.Forms.StatusBarPanel[] {
																							  this.NbNodesPanel,
																							  this.NbArcsPanel,
																							  this.CoordsPanel});
			this.GraphStatusBar.ShowPanels = true;
			this.GraphStatusBar.Size = new System.Drawing.Size(744, 24);
			this.GraphStatusBar.TabIndex = 1;
			this.GraphStatusBar.Text = "GraphStatusBar";
			// 
			// NbNodesPanel
			// 
			this.NbNodesPanel.AutoSize = System.Windows.Forms.StatusBarPanelAutoSize.Contents;
			this.NbNodesPanel.Icon = ((System.Drawing.Icon)(resources.GetObject("NbNodesPanel.Icon")));
			this.NbNodesPanel.Text = "NbNodes";
			this.NbNodesPanel.ToolTipText = "Number of nodes";
			this.NbNodesPanel.Width = 82;
			// 
			// NbArcsPanel
			// 
			this.NbArcsPanel.AutoSize = System.Windows.Forms.StatusBarPanelAutoSize.Contents;
			this.NbArcsPanel.Icon = ((System.Drawing.Icon)(resources.GetObject("NbArcsPanel.Icon")));
			this.NbArcsPanel.Text = "NbArcs";
			this.NbArcsPanel.ToolTipText = "Number of arcs";
			this.NbArcsPanel.Width = 72;
			// 
			// CoordsPanel
			// 
			this.CoordsPanel.Alignment = System.Windows.Forms.HorizontalAlignment.Right;
			this.CoordsPanel.AutoSize = System.Windows.Forms.StatusBarPanelAutoSize.Spring;
			this.CoordsPanel.BorderStyle = System.Windows.Forms.StatusBarPanelBorderStyle.None;
			this.CoordsPanel.Text = "Coordinates";
			this.CoordsPanel.Width = 574;
			// 
			// GraphPanel
			// 
			this.GraphPanel.BackColor = System.Drawing.Color.FromArgb(((System.Byte)(255)), ((System.Byte)(255)), ((System.Byte)(192)));
			this.GraphPanel.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
			this.GraphPanel.Dock = System.Windows.Forms.DockStyle.Fill;
			this.GraphPanel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
			this.GraphPanel.Location = new System.Drawing.Point(0, 24);
			this.GraphPanel.Name = "GraphPanel";
			this.GraphPanel.Size = new System.Drawing.Size(744, 470);
			this.GraphPanel.TabIndex = 2;
			this.GraphPanel.MouseUp += new System.Windows.Forms.MouseEventHandler(this.GraphPanel_MouseUp);
			this.GraphPanel.Paint += new System.Windows.Forms.PaintEventHandler(this.GraphPanel_Paint);
			this.GraphPanel.MouseMove += new System.Windows.Forms.MouseEventHandler(this.GraphPanel_MouseMove);
			this.GraphPanel.MouseLeave += new System.EventHandler(this.GraphPanel_MouseLeave);
			this.GraphPanel.MouseDown += new System.Windows.Forms.MouseEventHandler(this.GraphPanel_MouseDown);
			// 
			// FichierToolBar
			// 
			this.FichierToolBar.Appearance = System.Windows.Forms.ToolBarAppearance.Flat;
			this.FichierToolBar.Buttons.AddRange(new System.Windows.Forms.ToolBarButton[] {
																							  this.BoutonNouveau,
																							  this.BoutonCharger,
																							  this.BoutonSauver,
																							  this.BoutonAProposDe,
																							  this.Sep1});
			this.FichierToolBar.ButtonSize = new System.Drawing.Size(16, 16);
			this.FichierToolBar.Divider = false;
			this.FichierToolBar.Dock = System.Windows.Forms.DockStyle.None;
			this.FichierToolBar.DropDownArrows = true;
			this.FichierToolBar.ImageList = this.ImagesFichier;
			this.FichierToolBar.Name = "FichierToolBar";
			this.FichierToolBar.ShowToolTips = true;
			this.FichierToolBar.Size = new System.Drawing.Size(104, 23);
			this.FichierToolBar.TabIndex = 0;
			this.FichierToolBar.ButtonClick += new System.Windows.Forms.ToolBarButtonClickEventHandler(this.FichierToolBar_ButtonClick);
			// 
			// BoutonNouveau
			// 
			this.BoutonNouveau.ImageIndex = 0;
			this.BoutonNouveau.Tag = 0;
			this.BoutonNouveau.ToolTipText = "Clear the current graph to create a new one";
			// 
			// BoutonCharger
			// 
			this.BoutonCharger.ImageIndex = 1;
			this.BoutonCharger.Tag = 1;
			this.BoutonCharger.ToolTipText = "Load a graph";
			// 
			// BoutonSauver
			// 
			this.BoutonSauver.ImageIndex = 2;
			this.BoutonSauver.Tag = 2;
			this.BoutonSauver.ToolTipText = "Save the current graph";
			// 
			// BoutonAProposDe
			// 
			this.BoutonAProposDe.ImageIndex = 3;
			this.BoutonAProposDe.Tag = 3;
			this.BoutonAProposDe.ToolTipText = "About GraphFormer...";
			// 
			// Sep1
			// 
			this.Sep1.Style = System.Windows.Forms.ToolBarButtonStyle.Separator;
			// 
			// ImagesFichier
			// 
			this.ImagesFichier.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
			this.ImagesFichier.ImageSize = new System.Drawing.Size(16, 16);
			this.ImagesFichier.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("ImagesFichier.ImageStream")));
			this.ImagesFichier.TransparentColor = System.Drawing.Color.Transparent;
			// 
			// AEtoileToolBar
			// 
			this.AEtoileToolBar.Appearance = System.Windows.Forms.ToolBarAppearance.Flat;
			this.AEtoileToolBar.Buttons.AddRange(new System.Windows.Forms.ToolBarButton[] {
																							  this.Sep2,
																							  this.AEtoileDebut,
																							  this.AEtoileEtape,
																							  this.AEtoileFin});
			this.AEtoileToolBar.ButtonSize = new System.Drawing.Size(16, 16);
			this.AEtoileToolBar.Cursor = System.Windows.Forms.Cursors.Arrow;
			this.AEtoileToolBar.Divider = false;
			this.AEtoileToolBar.Dock = System.Windows.Forms.DockStyle.None;
			this.AEtoileToolBar.DropDownArrows = true;
			this.AEtoileToolBar.ImageList = this.ImagesPasAPas;
			this.AEtoileToolBar.Location = new System.Drawing.Point(240, 0);
			this.AEtoileToolBar.Name = "AEtoileToolBar";
			this.AEtoileToolBar.ShowToolTips = true;
			this.AEtoileToolBar.Size = new System.Drawing.Size(80, 23);
			this.AEtoileToolBar.TabIndex = 3;
			this.AEtoileToolBar.ButtonClick += new System.Windows.Forms.ToolBarButtonClickEventHandler(this.AEtoileToolBar_ButtonClick);
			// 
			// Sep2
			// 
			this.Sep2.Style = System.Windows.Forms.ToolBarButtonStyle.Separator;
			// 
			// AEtoileDebut
			// 
			this.AEtoileDebut.ImageIndex = 0;
			this.AEtoileDebut.Tag = 0;
			this.AEtoileDebut.ToolTipText = "Initialize A*";
			// 
			// AEtoileEtape
			// 
			this.AEtoileEtape.ImageIndex = 1;
			this.AEtoileEtape.Tag = 1;
			this.AEtoileEtape.ToolTipText = "Perform A*\'s next step";
			// 
			// AEtoileFin
			// 
			this.AEtoileFin.ImageIndex = 2;
			this.AEtoileFin.Tag = 2;
			this.AEtoileFin.ToolTipText = "Perform A* to the end";
			// 
			// ImagesPasAPas
			// 
			this.ImagesPasAPas.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
			this.ImagesPasAPas.ImageSize = new System.Drawing.Size(16, 16);
			this.ImagesPasAPas.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("ImagesPasAPas.ImageStream")));
			this.ImagesPasAPas.TransparentColor = System.Drawing.Color.Transparent;
			// 
			// LabelAide
			// 
			this.LabelAide.Dock = System.Windows.Forms.DockStyle.Top;
			this.LabelAide.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
			this.LabelAide.ForeColor = System.Drawing.SystemColors.InactiveCaption;
			this.LabelAide.Name = "LabelAide";
			this.LabelAide.Size = new System.Drawing.Size(744, 24);
			this.LabelAide.TabIndex = 4;
			this.LabelAide.Text = "Aide";
			this.LabelAide.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
			// 
			// GraphFormer
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(744, 518);
			this.Controls.AddRange(new System.Windows.Forms.Control[] {
																		  this.GraphPanel,
																		  this.GraphStatusBar,
																		  this.FichierToolBar,
																		  this.EditionToolBar,
																		  this.AEtoileToolBar,
																		  this.LabelAide});
			this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
			this.Name = "GraphFormer";
			this.Text = "GraphFormer";
			((System.ComponentModel.ISupportInitialize)(this.NbNodesPanel)).EndInit();
			((System.ComponentModel.ISupportInitialize)(this.NbArcsPanel)).EndInit();
			((System.ComponentModel.ISupportInitialize)(this.CoordsPanel)).EndInit();
			this.ResumeLayout(false);

		}
		#endregion

		#region Etats

		bool BoutonDEnfonce = false;
		bool BoutonGEnfonce = false;

		Graph G;
		Node TempN1, TempN2;
		bool AjouterN1, AjouterN2;
		AStar AE;
		Node NDepart, NArrivee;
		Node[] Chemin;
		Point TempP;
		int NbHeuristique = 0;
		
		bool _PasAPas;
		bool PasAPas
		{
			get { return _PasAPas; }
			set
			{
				_PasAPas = value;
				if ( AdapterAEtoile() ) GraphPanel.Invalidate();
				MenuContextuel.MenuItems[0].Checked = !_PasAPas;
				MenuContextuel.MenuItems[1].Checked = _PasAPas;
				AEtoileToolBar.Visible = _PasAPas;
			}
		}

		bool CalculPossible { get { return NDepart!=null && NArrivee!=null; } }

		enum Action { Dessiner, Effacer, Deplacer, ChangerEtat, AEtoile }
		Action _Mode;
		Action Mode
		{
			get { return _Mode; }
			set
			{
				_Mode = value;
				foreach ( ToolBarButton B in EditionToolBar.Buttons ) B.Pushed = false; // Actions exclusives
				EditionToolBar.Buttons[(int)_Mode].Pushed = true;
				switch( _Mode )
				{
					case Action.AEtoile:
					{
						LabelAide.Text = "Choose starting and ending nodes with the respective left and right mouse buttons";
						AfficherInfosResultat();
						break;
					}
					case Action.ChangerEtat:
					{
						LabelAide.Text = "Select a node or an arc to activate/deactivate";
						break;
					}
					case Action.Deplacer:
					{
						LabelAide.Text = "Select a node with left button to move it (right button to move the entire graph)";
						break;
					}
					case Action.Dessiner:
					{
						LabelAide.Text = "Clic and drag to draw an arc between 2 nodes (right button for bidirectional arcs)";
						break;
					}
					case Action.Effacer:
					{
						LabelAide.Text = "Chose nodes to delete (clic or select a rectangle)";
						break;
					}
				}
			}
		}
		#endregion

		#region Methodes outils
		static Rectangle Boite(params Node[] NoeudsAEnglober)
		{
			Rectangle R = RectangleCentres(NoeudsAEnglober);
			Size S = new Size(Rayon+2*Epaisseur, Rayon+2*Epaisseur);
			R.Inflate(S);
			return R;
		}

		static Rectangle RectangleCentres(params Node[] NoeudsAEnglober)
		{
			double[] Min, Max;
			Node.BoundingBox(NoeudsAEnglober, out Min, out Max);
			return Rectangle.FromLTRB((int)Min[0], (int)Min[1], (int)Max[0], (int)Max[1]);
		}

		static bool Collision(Node N1, Node N2)
		{
			return Node.SquareEuclidianDistance(N1, N2)<=Rayon*Rayon;
		}

		bool NoeudSelonPosition(int X, int Y, ref Node N)
		{
			Node NSJ = NoeudSousJacent(X, Y);
			if ( NSJ!=null )
			{
				N = NSJ;
				return false;
			}
			else
			{
				if (N==null) N = new Node(X, Y, 0);
				else N.ChangeXYZ(X, Y, 0);
				return true;
			}
		}

		Node NoeudSousJacent(int X, int Y)
		{
			double Distance;
			Node ClosestNode = G.ClosestNode(X, Y, 0, out Distance, false);
			return Distance<=2*Rayon ? ClosestNode : null;
		}

		Arc ArcSousJacent(int X, int Y)
		{
			double Distance;
			Arc ArcPlusProche = G.ClosestArc(X, Y, 0, out Distance, false);
			return Distance<=Rayon ? ArcPlusProche : null;
		}
		#endregion

		#region Réponses aux actions
		private void GraphToolBar_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e)
		{
			Mode = (Action)e.Button.Tag;
		}

		private void ChoixAutomatique(object sender, EventArgs e) { PasAPas = false; }
		private void ChoixPasAPas(object sender, EventArgs e) { PasAPas = true; }

		private void GraphPanel_MouseLeave(object sender, System.EventArgs e)
		{
			if ( !CalculPossible ) CoordsPanel.Text = String.Empty;
		}

		private void GraphPanel_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			if ( e.Button==MouseButtons.Left ) BoutonGEnfonce = true;
			else if ( e.Button==MouseButtons.Right ) BoutonDEnfonce = true;
			else return;
			if ( BoutonGEnfonce && BoutonDEnfonce ) return;

			TempN1 = TempN2 = null;
			switch ( Mode )
			{
				case Action.Effacer:
				case Action.Dessiner:
				{
					AjouterN1 = NoeudSelonPosition(e.X, e.Y, ref TempN1);
					TempN2 = new Node(TempN1.X, TempN1.Y, 0);
					GraphPanel.Invalidate( Boite(TempN1, TempN2) );
					break;
				}
				case Action.Deplacer:
				{
					TempP = new Point(e.X, e.Y);
					TempN1 = NoeudSousJacent(e.X, e.Y);
					break;
				}
				default: break;
			}
		}

		string SB_Point(int X, int Y) { return "{"+X+";"+Y+"}"; }
		string SB_Noeud(int X, int Y) { return "Node "+SB_Point(X, Y); }
		string SB_TempArc(int X, int Y, bool DoubleSens)
		{
			string Fleche = DoubleSens ? " <-> " : " -> ";
			Node N = NoeudSousJacent(X, Y);
			string Cible = N!=null ? SB_Noeud((int)N.X, (int)N.Y) : SB_Point((int)TempN2.X, (int)TempN2.Y);
			return SB_Point((int)TempN1.X, (int)TempN1.Y)+Fleche+Cible+" : Length = "+(int)Node.EuclidianDistance(TempN1, TempN2);
		}
		string SB_TempRectangle()
		{
			return SB_Point((int)TempN1.X, (int)TempN1.Y)+" + "+SB_Point((int)Math.Abs(TempN1.X-TempN2.X), (int)Math.Abs(TempN1.Y-TempN2.Y));
		}
		string SB_DetecterNoeud(int X, int Y)
		{
			Node N = NoeudSousJacent(X, Y);
			return N!=null ? SB_Noeud((int)N.X, (int)N.Y) : SB_Point(X, Y);
		}
		string SB_DetecterNoeudOuArc(int X, int Y)
		{
			Node N = NoeudSousJacent(X, Y);
			if ( N!=null ) return SB_Noeud((int)N.X, (int)N.Y);
			Arc A = ArcSousJacent(X, Y);
			if ( A!=null ) return "Arc "+SB_Point((int)A.StartNode.X, (int)A.StartNode.Y)+" -> "+SB_Point((int)A.EndNode.X, (int)A.EndNode.Y);
			return SB_Point(X, Y);
		}
		string SB_NoeudPlusProche(int X, int Y)
		{
			double Distance;
			Node N = G.ClosestNode(X, Y, 0, out Distance, true);
			return N!=null ? SB_Noeud((int)N.X, (int)N.Y) : SB_Point(X, Y);
		}

		void StatusBarMouseMove(System.Windows.Forms.MouseEventArgs e)
		{
			int X = e.X;
			int Y = e.Y;
			bool BoutonEnfonce = e.Button==MouseButtons.Left || e.Button==MouseButtons.Right;

			switch( Mode )
			{
				case Action.Dessiner:
				{
					CoordsPanel.Text = BoutonEnfonce ? SB_TempArc(X, Y, e.Button==MouseButtons.Right) : SB_DetecterNoeud(X, Y);
					break;
				}
				case Action.Effacer:
				{
					CoordsPanel.Text = BoutonEnfonce ? SB_TempRectangle() : SB_DetecterNoeudOuArc(X, Y);
					break;
				}
				case Action.Deplacer:
				{
					if ( e.Button==MouseButtons.Right ) CoordsPanel.Text = String.Empty;
					else
					{
						if ( BoutonEnfonce )
							CoordsPanel.Text = TempN1!=null ? SB_Noeud(X, Y) : SB_Point(X, Y);
						else
							CoordsPanel.Text = SB_DetecterNoeud(X, Y);
					}
					break;
				}
				case Action.ChangerEtat:
				{
					CoordsPanel.Text = SB_DetecterNoeudOuArc(X, Y);
					break;
				}
				case Action.AEtoile:
				{
					string S = String.Empty;
					if ( NDepart==null && NArrivee==null ) S = "Select STARTING and ENDING nodes";
					else if ( NDepart==null ) S = "Select STARTING node (with left button)";
					else if ( NArrivee==null ) S = "Select ENDING node (with right button)";
					CoordsPanel.Text = S+". Current : "+SB_NoeudPlusProche(X, Y);
					break;
				}
			}
		}

		private void GraphPanel_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			if ( BoutonGEnfonce && BoutonDEnfonce ) return;
			switch( Mode )
			{
				case Action.Effacer:
				case Action.Dessiner:
				{
					if ( e.Button==MouseButtons.Left || e.Button==MouseButtons.Right )
					{
						if ( TempN1==null || TempN2==null ) return;
						Rectangle AncienRect = Boite(TempN1, TempN2);
						TempN2.ChangeXYZ(e.X, e.Y, 0);
						Rectangle NouveauRect = Boite(TempN1, TempN2);
						GraphPanel.Invalidate( Rectangle.Union(AncienRect, NouveauRect) );
					}
					break;
				}
				case Action.Deplacer:
				{
					if ( e.Button==MouseButtons.Left )
					{
						if ( TempN1==null ) break;
						Rectangle AncienRect = Boite(TempN1.Molecule);

						Node[] AncienChemin = null;
						if ( Chemin!=null )
						{
							AncienChemin = new Node[Chemin.Length];
							for ( int i=0; i<AncienChemin.Length; i++ ) AncienChemin[i] = (Node)Chemin[i].Clone();
						}

						TempN1.ChangeXYZ(e.X, e.Y, 0);
						Rectangle NouveauRect = Boite(TempN1.Molecule);
						if ( AdapterAEtoile() && CheminsDifferents(AncienChemin, Chemin) ) GraphPanel.Invalidate();
						else GraphPanel.Invalidate( Rectangle.Union(AncienRect, NouveauRect) );
					}
					else if ( e.Button==MouseButtons.Right )
					{
						int DX = (int)(e.X-TempP.X);
						int DY = (int)(e.Y-TempP.Y);
						TempP.X = e.X;
						TempP.Y = e.Y;
						foreach ( Node N in G.Nodes ) N.ChangeXYZ(N.X+DX, N.Y+DY, 0);
						GraphPanel.Invalidate();
					}
					break;
				}
				default: break;
			}
			if ( !CalculPossible ) StatusBarMouseMove(e);
		}

		private void GraphPanel_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			bool Retour = BoutonGEnfonce && BoutonDEnfonce;
			if ( e.Button==MouseButtons.Left ) BoutonGEnfonce = false;
			else if ( e.Button==MouseButtons.Right ) BoutonDEnfonce = false;
			else return;
			if ( Retour ) return;

			switch( Mode )
			{
				case Action.Dessiner:
				{
					if ( TempN1==null || TempN2==null ) return;
					bool AjouterArc = false;
					Rectangle AncienRect = Boite(TempN1, TempN2);
					AjouterN2 = NoeudSelonPosition(e.X, e.Y, ref TempN2);
					if ( AjouterN1 ) { TempN1.Isolate(); G.AddNode(TempN1); }
					if ( !Collision(TempN1, TempN2) )
					{
						if ( AjouterN2 ) { TempN2.Isolate(); G.AddNode(TempN2); }
						if ( e.Button == MouseButtons.Left )
							G.AddArc(TempN1, TempN2, 1);
						else if ( e.Button == MouseButtons.Right ) G.Add2Arcs(TempN1, TempN2, 1);
						NbArcsPanel.Text = G.Arcs.Count.ToString();
						AjouterArc = true;
					}
					NbNodesPanel.Text = G.Nodes.Count.ToString();

					if ( AjouterArc && ( !AjouterN1 || !AjouterN2 ) && AdapterAEtoile() ) GraphPanel.Invalidate();
					else
					{
						Rectangle NouveauRect = Boite(TempN1, TempN2);
						GraphPanel.Invalidate( Rectangle.Union(AncienRect, NouveauRect) );
					}
					break;
				}
				case Action.Effacer:
				{
					if ( TempN1==null || TempN2==null ) return;
					bool Selection = false;
					Rectangle Zone = RectangleCentres(TempN1, TempN2);
					Zone.Inflate(1,1);
					Region Invalide = new Region(Zone);
					if ( Zone.Size.Width<2*Rayon && Zone.Size.Height<2*Rayon )
					{
						Node N = NoeudSousJacent(e.X, e.Y);
						if ( N!=null )
						{
							TestAEtoile(N, ref Invalide);
							Invalide.Union(Boite(N.Molecule));
							G.RemoveNode(N);
							NbNodesPanel.Text = G.Nodes.Count.ToString();
							NbArcsPanel.Text = G.Arcs.Count.ToString();
							Selection = true;
						}
						else
						{
							Arc A = ArcSousJacent(e.X, e.Y);
							if ( A!=null )
							{
								Invalide.Union( Boite(A.StartNode, A.EndNode) );
								G.RemoveArc(A);
								NbArcsPanel.Text = G.Arcs.Count.ToString();
								Selection = true;
							}
						}
					}
					else
					{
						ArrayList ListeNoeuds = new ArrayList();
						foreach ( Node N in G.Nodes )
						{
							if ( Zone.Contains(new Point((int)N.X, (int)N.Y)) )
							{
								TestAEtoile(N, ref Invalide);
								Invalide.Union( Boite(N.Molecule) );
								ListeNoeuds.Add(N);
								Selection = true;
							}
						}
						foreach ( Node N in ListeNoeuds ) G.RemoveNode(N);
						NbNodesPanel.Text = G.Nodes.Count.ToString();
						NbArcsPanel.Text = G.Arcs.Count.ToString();
					}

					if ( Selection && AdapterAEtoile() ) GraphPanel.Invalidate();
					else GraphPanel.Invalidate(Invalide);
					break;
				}
				case Action.ChangerEtat:
				{
					Node N = NoeudSousJacent(e.X, e.Y);
					Region Invalide = null;
					if ( N!=null )
					{
						N.Passable = !N.Passable;
						Invalide = new Region(Boite(N.Molecule));
					}
					else
					{
						Arc A = ArcSousJacent(e.X, e.Y);
						if ( A!=null )
						{
							A.Passable = !A.Passable;
							Invalide = new Region(Boite(A.StartNode, A.EndNode));
						}
					}

					if ( Invalide!=null )
					{
						if ( AdapterAEtoile() ) GraphPanel.Invalidate();
						else GraphPanel.Invalidate(Invalide);
					}
					break;
				}
				case Action.AEtoile:
				{
					double Distance;
					Node NoeudPlusProche = G.ClosestNode(e.X, e.Y, 0, out Distance, true);
					if ( NoeudPlusProche==null ) break;
					Rectangle Invalide = Boite(NoeudPlusProche);

					if ( NDepart!=null ) Invalide = Rectangle.Union(Invalide, Boite(NDepart));
					if ( NArrivee!=null ) Invalide = Rectangle.Union(Invalide, Boite(NArrivee));

					if ( e.Button==MouseButtons.Left ) NDepart = NDepart==NoeudPlusProche ? null : NoeudPlusProche;
					else if ( e.Button==MouseButtons.Right ) NArrivee = NArrivee==NoeudPlusProche ? null : NoeudPlusProche;

					if ( AdapterAEtoile() ) GraphPanel.Invalidate();
					else
					{
						if ( Chemin!=null )
						{
							Chemin = null;
							GraphPanel.Invalidate();
						}
						else GraphPanel.Invalidate(Invalide);
					}
					break;
				}
				default: break;
			}
			TempN1 = TempN2 = null;
		}

		// retourne true s'il faut invalider tout le panel
		bool AdapterAEtoile()
		{
			if ( !PasAPas )
			{
				if ( !CalculPossible ) return false;
				AEtoile_Fin();
				return true;
			}
			else
			{
				bool IlResteDesPas  = AE.Closed.Length>0 || AE.Open.Length>1;
				AEtoile_Debut();
				return IlResteDesPas;
			}
		}

		bool CheminsDifferents(Node[] C1, Node[] C2)
		{
			if ( C1==null || C2==null || C1.Length!=C2.Length ) return true;
			for ( int i=0; i<C1.Length; i++) if ( !C1[i].Equals(C2[i]) ) return true;
			return false;
		}

		void TestAEtoile(Node N, ref Region ZoneInvalide)
		{
			if ( N!=null )
			{
				if ( N==NDepart ) NDepart=null;
				if ( N==NArrivee ) NArrivee=null;
				if ( Chemin!=null && Array.IndexOf(Chemin, N)>=0 )
				{
					Chemin=null;
					ZoneInvalide.Union(new Rectangle(new Point(0,0),GraphPanel.Size));				
				}
			}
		}

		private void AEtoileToolBar_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e)
		{
			if ( !CalculPossible ) MessageBox.Show(
									   @"Before performing A* you must choose the starting and ending nodes
with the respective left and right mouse buttons.", "Impossible action", MessageBoxButtons.OK, MessageBoxIcon.Stop);

			switch( (int)e.Button.Tag )
			{
				case 0:
				{
					AEtoile_Debut();
					break;
				}
				case 1:
				{
					AEtoile_Etape();
					break;
				}
				case 2:
				{
					AEtoile_Fin();
					break;
				}
			}
			GraphPanel.Invalidate();
		}

		private void AEtoile_Debut()
		{
			if ( !CalculPossible )
			{
				foreach ( ToolBarButton B in AEtoileToolBar.Buttons ) B.Enabled = false;
				return;
			}
			else
			{
				AEtoileDebut.Enabled = false;
				AEtoileFin.Enabled = true;
				AEtoileEtape.Enabled = true;
				Chemin = null;
				AE.Initialize(NDepart, NArrivee);
				AfficherInfosResultat();
			}
		}

		private void AEtoile_Etape()
		{
			if ( !CalculPossible ) return;
			if ( !AE.NextStep() )
			{
				Chemin = AE.PathByNodes;
				AEtoileFin.Enabled = false;
				AEtoileEtape.Enabled = false;
			}
			AfficherInfosResultat();
			AEtoileDebut.Enabled = true;
		}

		private void AEtoile_Fin()
		{
			if ( !CalculPossible ) return;
			Chemin = AE.SearchPath(NDepart, NArrivee) ? AE.PathByNodes : null;
			AfficherInfosResultat();
			AEtoileDebut.Enabled = true;
			AEtoileFin.Enabled = false;
			AEtoileEtape.Enabled = false;
		}

		void AfficherInfosResultat()
		{
			if ( !CalculPossible ) return;

			else if ( Chemin==null )
			{
				if ( PasAPas ) CoordsPanel.Text = String.Format("Open list : {0} (green) ; Closed list : {1} (red)   -   Current step : {2}", AE.Open.Length, AE.Closed.Length, AE.StepCounter);
				else CoordsPanel.Text = "There is no possible path in this configuration.";
			}
			else 
			{
				int NbArcsParcourus;
				double Cout;
				if ( AE.PathFound )
				{
					AE.ResultInformation(out NbArcsParcourus, out Cout);
					CoordsPanel.Text = String.Format("Shortest path found in {0} step(s) : {1} arc(s) \\ cost = {2}", AE.StepCounter, NbArcsParcourus, (int)Cout);
				}
			}

			if ( PasAPas && AE.SearchEnded && !AE.PathFound )
			{
				GraphPanel.Invalidate();
				MessageBox.Show(
					@"In this configuration, there is no possible path. You can :
- either modify the graph
- or change the starting and/or ending nodes.", "No result", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
			}
		}

		private void FichierToolBar_ButtonClick(object sender, System.Windows.Forms.ToolBarButtonClickEventArgs e)
		{
			switch( (int)e.Button.Tag )
			{
				case 0:
				{
					if ( G.Nodes.Count>0 )
					{
						DialogResult DR = MessageBox.Show("You are about to clear the current graph. Do you confirm ?", "New graph", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
						if ( DR==DialogResult.No ) break;
					}
					G.Clear();
					NouveauGraphe();
					GraphPanel.Invalidate();
					break;
				}
				case 1:
				{
					if ( G.Nodes.Count>0 )
					{
						DialogResult DR = MessageBox.Show("You are about to replace the current graph with another. Do you confirm ?", "Load a graph", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
						if ( DR==DialogResult.No ) break;
					}
					if ( Charger() )
					{
						NouveauGraphe();
						GraphPanel.Invalidate();
					}
					break;
				}
				case 2:
				{
					if ( Sauver() ) GraphPanel.Invalidate();
					break;
				}
				case 3:
				{
					DialogueAPropos.DijkstraHeuristiqueBalance = AE.DijkstraHeuristicBalance;
					DialogueAPropos.HeuristiqueChoisie = NbHeuristique;

					DialogResult DR = DialogueAPropos.ShowDialog(this);
					if ( DR==DialogResult.OK ) AppliquerChangement(DialogueAPropos);
					break;
				}
			}
		}

		internal void AppliquerChangement(APropos DialogueAPropos)
		{
			AE.DijkstraHeuristicBalance = DialogueAPropos.DijkstraHeuristiqueBalance;
			if ( NbHeuristique != DialogueAPropos.HeuristiqueChoisie )
			{
				NbHeuristique = DialogueAPropos.HeuristiqueChoisie;
				switch ( NbHeuristique )
				{
					case 0: AE.ChoosenHeuristic = AStar.EuclidianHeuristic; break;
					case 1: AE.ChoosenHeuristic = AStar.ManhattanHeuristic; break;
					case 2: AE.ChoosenHeuristic = AStar.MaxAlongAxisHeuristic; break;
				}
			}
			AdapterAEtoile();
			GraphPanel.Invalidate();
		}

		void NouveauGraphe()
		{
			TempN1 = TempN2 = null;
			AjouterN1 = AjouterN2 = false;
			AE = new AStar(G);
			CoordsPanel.Text = String.Empty;;
			NbNodesPanel.Text = G.Nodes.Count.ToString();
			NbArcsPanel.Text = G.Arcs.Count.ToString();
			NDepart = NArrivee = null;
			Chemin = null;
			PasAPas = false;
			AEtoileToolBar.Visible = false;
		}
		#endregion

		#region Archivage
		bool Charger()
		{
			try
			{
				Stream StreamRead;
				OpenFileDialog DialogueCharger = new OpenFileDialog();
				if( DialogueCharger.ShowDialog() == DialogResult.OK )
				{
					if( (StreamRead = DialogueCharger.OpenFile())!= null )
					{
						BinaryFormatter BinaryRead = new BinaryFormatter();
						G = (Graph) BinaryRead.Deserialize(StreamRead);
						StreamRead.Close();
						return true;
					}
				}
			}
			catch {}
			return false;
		}

		bool Sauver()
		{
			try
			{
				Stream StreamWrite;
				SaveFileDialog DialogueSauver = new SaveFileDialog();
				if( DialogueSauver.ShowDialog() == DialogResult.OK )
				{
					if( (StreamWrite = DialogueSauver.OpenFile()) != null )
					{
						BinaryFormatter BinaryWrite = new BinaryFormatter();
						BinaryWrite.Serialize(StreamWrite, G);
						StreamWrite.Close();
						return true;
					}
				}
			}
			catch {}
			return false;
		}
		#endregion

		#region Dessin
		/// <summary>
		/// 
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void GraphPanel_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
		{
			Graphics Grfx = e.Graphics;
			Grfx.SmoothingMode = SmoothingMode.AntiAlias;
			Grfx.PixelOffsetMode = PixelOffsetMode.None;

			SuspendLayout();
			// Dessin du graphe
			foreach ( Node N in G.Nodes ) DessinerNoeud(Grfx, N.Passable?CrayonNoeuds:CrayonNoeudsInactifs, N);
			foreach ( Arc A in G.Arcs ) DessinerArc(Grfx, A.Passable?CrayonArcs:CrayonArcsInactifs, A);

			// Dessin du tracé temporaire courant
			if ( Mode==Action.Dessiner )
			{
				DessinerNoeud(Grfx, CrayonTemp, TempN1);
				DessinerNoeud(Grfx, CrayonTemp, TempN2);
				DessinerArc(Grfx, CrayonTemp, TempN1, TempN2);
			}
			else if ( Mode==Action.Effacer )
			{
				if ( TempN1!=null && TempN2!=null )
					Grfx.DrawRectangle(CrayonTemp, RectangleCentres(TempN1, TempN2));
			}

			DessinerDrapeau(Grfx, NDepart, 1);
			DessinerDrapeau(Grfx, NArrivee, 2);

			// Dessins des noeuds "pas à pas"
			if ( PasAPas && CalculPossible )
			{
				Node[] DerniersNoeudsClosed = new Node[AE.Closed.Length];
				for ( int i=0; i<AE.Closed.Length; i++ )
				{
					DerniersNoeudsClosed[i] = AE.Closed[i][AE.Closed[i].Length-1];
					DessinerNoeudPlein(Grfx, Brushes.Red, DerniersNoeudsClosed[i]);
				}
				if (Chemin==null)
				{
					for ( int i=0; i<AE.Open.Length; i++ )
					{
						Node[] Nodes = AE.Open[i];
						int L = Nodes.Length;
						Node DernierNoeud = Nodes[L-1];
						if ( Array.IndexOf(DerniersNoeudsClosed, DernierNoeud)>=0 )
							DessinerNoeudMoitie(Grfx, Brushes.LawnGreen, DernierNoeud);
						else
							DessinerNoeudPlein(Grfx, Brushes.LimeGreen, DernierNoeud);
					}
					for ( int i=0; i<AE.Open.Length; i++ )
					{
						Node[] Nodes = AE.Open[i];
						int L = Nodes.Length;
						if ( L>1 )
						{
							DessinerArc(Grfx, CrayonArcsPas, Nodes[L-2], Nodes[L-1]);
							DessinerNumero(Grfx, Nodes[L-2], Nodes[L-1], i+1);
						}
					}
				}
			}
			
			// Dessins AEtoile
			if ( Chemin!=null ) DessinerChemin(Grfx, CrayonChemin, Chemin);
			ResumeLayout(false);
		}

		static private void DessinerNoeud(Graphics Grfx, Pen P, Node N)
		{
			if ( N==null ) return;
			Grfx.DrawEllipse(P, (int)N.X-Rayon, (int)N.Y-Rayon, 2*Rayon+1, 2*Rayon+1);
		}

		static private void DessinerArc(Graphics Grfx, Pen P, Arc A)
		{
			DessinerArc(Grfx, P, A.StartNode, A.EndNode);
		}

		static private void DessinerArc(Graphics Grfx, Pen P, Node N1, Node N2)
		{
			if ( N1==null || N2==null ) return;
			Grfx.DrawLine(P, (int)N1.X, (int)N1.Y, (int)N2.X, (int)N2.Y);
		}

		static private void DessinerNoeudPlein(Graphics Grfx, Brush B, Node N)
		{
			if ( N==null ) return;
			Rectangle R = new Rectangle((int)N.X-Rayon, (int)N.Y-Rayon, 2*Rayon+1, 2*Rayon+1);
			Grfx.FillEllipse(B, R);
		}

		static private void DessinerNoeudMoitie(Graphics Grfx, Brush B, Node N)
		{
			if ( N==null ) return;
			Rectangle R = new Rectangle((int)N.X-Rayon, (int)N.Y-Rayon, 2*Rayon+1, 2*Rayon+1);
			Grfx.FillPie(B, R, 0, 180);
		}

		static private void DessinerNumero(Graphics Grfx, Node N1, Node N2, int i)
		{
			StringFormat F = new StringFormat();
			F.Alignment = StringAlignment.Center;
			F.LineAlignment = StringAlignment.Center;
			Rectangle R = RectangleCentres(N1, N2);
			Font Police = DefaultFont;
			int LargeurMin = (int)Police.GetHeight();
			R.Inflate(LargeurMin, LargeurMin);
			Grfx.DrawString(i.ToString(), Police, Brushes.Black, R, F);
		}

		static private void DessinerDrapeau(Graphics Grfx, Node N, int Numero)
		{
			if ( N==null ) return;
			Point[] Pts = new Point[5];

			double AnglePortion = (2*Math.PI)/Pts.Length;
			for ( int i=0; i<Pts.Length; i++ )
			{
				double Angle = 2*i*AnglePortion;
				if ( Numero==1 ) Angle += AnglePortion/2;
				Pts[i] = new Point(1+(int)(N.X+(Rayon+1)*Math.Cos(Angle)), 1+(int)(N.Y+(Rayon+1)*Math.Sin(Angle)));
			}
			GraphicsPath GP = new GraphicsPath();
			GP.AddLines(Pts);
			GP.FillMode = FillMode.Winding;
			Grfx.FillPath(Numero==1 ? Brushes.DarkTurquoise : Brushes.Blue, GP);
		}

		static private void DessinerChemin(Graphics Grfx, Pen P, Node[] C)
		{
			Point[] Pnts = new Point[C.Length];
			if ( Pnts.Length>1 )
			{
				for ( int i=0; i<Pnts.Length; i++ )
				{
					Pnts[i].X = (int)C[i].X;
					Pnts[i].Y = (int)C[i].Y;
				}
				Grfx.DrawCurve(P, Pnts);
			}
		}
		#endregion

		/// <summary>
		/// The main entry point for the application.
		/// </summary>
		[STAThread]
		static void Main() 
		{
			try
			{
				Application.Run(new GraphFormer());
			}
			catch (Exception e) { MessageBox.Show(e.ToString()); }
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
France France
Eric Marchesin is working as a software development engineer at Dassault Systèmes, Paris. Dassault Systèmes is a global leader in the market for Product Lifecycle Management using 3D modeling digital technology.
He has also been working for Video Game companies as well as Artificial Intelligence projects.
His programming experience more specifically includes C/C++, MFC, OpenGL, C# and .NET framework.

Of course I appreciate beautiful and fine algorithms. There's quite an art to creating powerful, effective and ergonomic programs.
Among many other things I enjoy music, sun, passion and generally whatever makes you imagine, travel and dream.

Comments and Discussions