26 using System.Collections.Generic;
28 using System.Xml.Linq;
30 using System.Globalization;
32 namespace Satsuma.Drawing
35 public struct PointD : IEquatable<PointD>
37 public double X {
get;
private set; }
38 public double Y {
get;
private set; }
49 return X == other.
X && Y == other.
Y;
52 public override bool Equals(
object obj)
54 if (!(obj is
PointD))
return false;
55 return Equals((PointD)obj);
68 public override int GetHashCode()
70 return (X.GetHashCode() * 17) + Y.GetHashCode();
73 public string ToString(IFormatProvider provider)
75 return string.Format(provider,
"({0} {1})", X, Y);
78 public override string ToString()
80 return ToString(CultureInfo.CurrentCulture);
95 public static explicit operator PointF(
PointD self)
97 return new PointF((
float)
self.X, (
float)
self.Y);
101 public static PointF ToPointF(
PointD self)
109 return Math.Sqrt((X - other.
X) * (X - other.
X) + (Y - other.
Y) * (Y - other.
Y));
141 public const double DefaultStartingTemperature = 0.2;
143 public const double DefaultMinimumTemperature = 0.01;
145 public const double DefaultTemperatureAttenuation = 0.95;
148 public IGraph Graph {
get;
private set; }
150 public Dictionary<Node, PointD> NodePositions {
get;
private set; }
156 public Func<double, double> SpringForce {
get;
set; }
162 public Func<double, double> ElectricForce {
get;
set; }
164 public double Temperature {
get;
set; }
166 public double TemperatureAttenuation {
get;
set; }
171 NodePositions =
new Dictionary<Node, PointD>();
172 SpringForce = (d => 2 * Math.Log(d));
173 ElectricForce = (d => 1 / (d * d));
174 TemperatureAttenuation = DefaultTemperatureAttenuation;
176 Initialize(initialPositions);
181 public void Initialize(Func<Node, PointD> initialPositions = null)
183 if (initialPositions == null)
186 Random r =
new Random();
187 initialPositions = (node =>
new PointD(r.NextDouble(), r.NextDouble()));
190 foreach (var node
in Graph.Nodes())
191 NodePositions[node] = initialPositions(node);
194 Temperature = DefaultStartingTemperature;
200 Dictionary<Node, PointD> forces =
new Dictionary<Node, PointD>();
202 foreach (var u
in Graph.Nodes())
204 PointD uPos = NodePositions[u];
205 double xForce = 0, yForce = 0;
207 foreach (var arc
in Graph.Arcs(u))
209 PointD vPos = NodePositions[Graph.Other(arc, u)];
211 double force = Temperature * SpringForce(d);
212 xForce += (vPos.
X - uPos.X) / d * force;
213 yForce += (vPos.
Y - uPos.Y) / d * force;
216 foreach (var v
in Graph.Nodes())
218 if (v == u)
continue;
219 PointD vPos = NodePositions[v];
221 double force = Temperature * ElectricForce(d);
222 xForce += (uPos.X - vPos.
X) / d * force;
223 yForce += (uPos.Y - vPos.
Y) / d * force;
225 forces[u] =
new PointD(xForce, yForce);
228 foreach (var node
in Graph.Nodes())
229 NodePositions[node] += forces[node];
230 Temperature *= TemperatureAttenuation;
234 public void Run(
double minimumTemperature = DefaultMinimumTemperature)
236 while (Temperature > minimumTemperature) Step();