PageRenderTime 60ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/Vis/Graph.xaml.cs

https://github.com/satomacoto/Silverlight-Graph
C# | 583 lines | 379 code | 90 blank | 114 comment | 7 complexity | 2c608e40547dff8080beef05d4e3318a MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Net;
  6. using System.Runtime.Serialization;
  7. using System.Runtime.Serialization.Json;
  8. using System.Text;
  9. using System.Windows;
  10. using System.Windows.Controls;
  11. using System.Windows.Data;
  12. using System.Windows.Documents;
  13. using System.Windows.Input;
  14. using System.Windows.Markup;
  15. using System.Windows.Media;
  16. using System.Windows.Media.Animation;
  17. using System.Windows.Shapes;
  18. using System.Xml.Linq;
  19. using Physics;
  20. using System.Windows.Browser;
  21. namespace Vis
  22. {
  23. public partial class Graph : UserControl
  24. {
  25. #region Fields (5) 
  26. bool isMouseCaptured;
  27. Point mousePosition;
  28. public Dictionary<string, Particle> particles;
  29. Simulation sim;
  30. public List<Spring> springs;
  31. #endregion Fields 
  32. #region Constructors (2) 
  33. public Graph(IDictionary<string, string> initParams)
  34. {
  35. InitializeComponent();
  36. Initialize();
  37. Loaded += new RoutedEventHandler(Graph_Loaded);
  38. // Get initial values
  39. string json;
  40. if (initParams.TryGetValue("json", out json))
  41. // json should be urlencoded.
  42. LoadJson(HttpUtility.UrlDecode(json));
  43. //string xml;
  44. //if (initParams.TryGetValue("xml", out xml))
  45. // LoadXml(initParams["xml"]);
  46. }
  47. public Graph()
  48. {
  49. InitializeComponent();
  50. Initialize();
  51. Loaded += new RoutedEventHandler(Graph_Loaded);
  52. }
  53. #endregion Constructors 
  54. #region Properties (4) 
  55. /// <summary>
  56. /// The drag co-efficient.
  57. /// </summary>
  58. public double drag
  59. {
  60. get { return sim.dragForce.drag; }
  61. set { sim.dragForce.drag = value; }
  62. }
  63. /// <summary>
  64. /// The gravitational constant to use. Negative value produce a repulsive force.
  65. /// </summary>
  66. public double gravitation
  67. {
  68. get { return sim.nbodyForce.gravitation; }
  69. set { sim.nbodyForce.gravitation = value; }
  70. }
  71. /// <summary>
  72. /// The gravitational accelaration in the horizontal dimension.
  73. /// </summary>
  74. public double gx
  75. {
  76. get { return sim.gravityForce.gx; }
  77. set { sim.gravityForce.gx = value; }
  78. }
  79. /// <summary>
  80. /// The gravitational accelaration in the vertical dimension.
  81. /// </summary>
  82. public double gy
  83. {
  84. get { return sim.gravityForce.gy; }
  85. set { sim.gravityForce.gy = value; }
  86. }
  87. #endregion Properties 
  88. #region Methods (19) 
  89. // Public Methods (12) 
  90. /// <summary>
  91. /// Adds FrameworkElement edge.
  92. /// </summary>
  93. /// <param name="element"></param>
  94. /// <param name="p1"></param>
  95. /// <param name="p2"></param>
  96. /// <returns></returns>
  97. public Spring AddEdgeFrameworkElement(FrameworkElement element, double length, Particle p1, Particle p2)
  98. {
  99. var spring = sim.AddSpring(p1, p2, length, 0.1, 0.1);
  100. springs.Add(spring);
  101. LayoutRoot.Children.Insert(0, element);
  102. var group = new TransformGroup();
  103. var scale = new ScaleTransform();
  104. var rotate = new RotateTransform();
  105. var translate = new TranslateTransform();
  106. group.Children.Add(scale);
  107. group.Children.Add(rotate);
  108. group.Children.Add(translate);
  109. p1.PositionChanged += (s, e) =>
  110. {
  111. var dx = p2.x - p1.x;
  112. var dy = p2.y - p1.y;
  113. var d = Math.Sqrt(dx * dx + dy * dy);
  114. var angle = Math.Atan2(dy, dx) / Math.PI * 180.0;
  115. scale.ScaleX = d / length;
  116. rotate.Angle = angle;
  117. translate.X = p1.x;
  118. translate.Y = p1.y;
  119. };
  120. p2.PositionChanged += (s, e) =>
  121. {
  122. var dx = p2.x - p1.x;
  123. var dy = p2.y - p1.y;
  124. var d = Math.Sqrt(dx * dx + dy * dy);
  125. var angle = Math.Atan2(dy, dx) / Math.PI * 180.0;
  126. scale.ScaleX = d / length;
  127. rotate.Angle = angle;
  128. translate.X = p1.x;
  129. translate.Y = p1.y;
  130. };
  131. element.RenderTransform = group;
  132. return spring;
  133. }
  134. /// <summary>
  135. /// Adds a line edge.
  136. /// </summary>
  137. /// <param name="p1"></param>
  138. /// <param name="p2"></param>
  139. /// <returns></returns>
  140. public Spring AddEdgeLine(Particle p1, Particle p2, double restLength, double tension, double damping)
  141. {
  142. var s = sim.AddSpring(p1, p2, restLength, tension, damping);
  143. Line line = new Line()
  144. {
  145. Fill = new SolidColorBrush(Colors.Brown),
  146. Stroke = new SolidColorBrush(Colors.Brown),
  147. StrokeThickness = 1,
  148. StrokeEndLineCap = PenLineCap.Triangle
  149. };
  150. line.SetBinding(Line.X1Property, new Binding("x") { Source = p1, Mode = BindingMode.TwoWay });
  151. line.SetBinding(Line.Y1Property, new Binding("y") { Source = p1, Mode = BindingMode.TwoWay });
  152. line.SetBinding(Line.X2Property, new Binding("x") { Source = p2, Mode = BindingMode.TwoWay });
  153. line.SetBinding(Line.Y2Property, new Binding("y") { Source = p2, Mode = BindingMode.TwoWay });
  154. // adds
  155. LayoutRoot.Children.Insert(0, line);
  156. springs.Add(s);
  157. return s;
  158. }
  159. /// <summary>
  160. /// Adds a polygon edge.
  161. /// </summary>
  162. /// <param name="p1"></param>
  163. /// <param name="p2"></param>
  164. /// <param name="style"></param>
  165. /// <returns></returns>
  166. public Spring AddEdgePolygon(Particle p1, Particle p2)
  167. {
  168. Polygon poly = new Polygon()
  169. {
  170. Points = new PointCollection(){
  171. new Point(0.0,1.0),
  172. new Point(50.0,0.0),
  173. new Point(0.0,-1.0)
  174. },
  175. Stroke = new SolidColorBrush(Colors.Cyan),
  176. };
  177. return AddEdgeFrameworkElement(poly, 50.0, p1, p2);
  178. }
  179. public Spring AddEdgeTriangle(Particle p1, Particle p2,
  180. double restLength, double tension, double damping,
  181. double baseWidth,
  182. string fill, string stroke, double strokeThickness)
  183. {
  184. var s = sim.AddSpring(p1, p2, restLength, tension, damping);
  185. Triangle tri = new Triangle()
  186. {
  187. Fill = new SolidColorBrush(Str2Color(fill)),
  188. Stroke = new SolidColorBrush(Str2Color(stroke)),
  189. StrokeThickness = strokeThickness,
  190. BaseWidth = baseWidth
  191. };
  192. tri.SetBinding(Triangle.X1Property, new Binding("x") { Source = p1, Mode = BindingMode.TwoWay });
  193. tri.SetBinding(Triangle.Y1Property, new Binding("y") { Source = p1, Mode = BindingMode.TwoWay });
  194. tri.SetBinding(Triangle.X2Property, new Binding("x") { Source = p2, Mode = BindingMode.TwoWay });
  195. tri.SetBinding(Triangle.Y2Property, new Binding("y") { Source = p2, Mode = BindingMode.TwoWay });
  196. // adds
  197. LayoutRoot.Children.Insert(0, tri);
  198. //LayoutRoot.Children.Add(tri);
  199. springs.Add(s);
  200. return s;
  201. }
  202. /// <summary>
  203. /// Adds FrameworkElement node.
  204. /// </summary>
  205. /// <param name="element">FrameworkElement</param>
  206. /// <param name="name"></param>
  207. /// <param name="x"></param>
  208. /// <param name="y"></param>
  209. /// <returns></returns>
  210. public Particle AddNodeFrameworkElement(FrameworkElement element, string name, double mass, double x, double y)
  211. {
  212. // creates a new Particle instance
  213. var p = sim.AddParticle(mass, x, y);
  214. // sets binding
  215. element.DataContext = p;
  216. element.SetBinding(Canvas.LeftProperty, new Binding("x") { Mode = BindingMode.TwoWay });
  217. element.SetBinding(Canvas.TopProperty, new Binding("y") { Mode = BindingMode.TwoWay });
  218. // sets mouse event
  219. element.MouseLeftButtonDown += (s, e) =>
  220. {
  221. p.IsActive = false;
  222. };
  223. element.MouseLeftButtonUp += (s, e) =>
  224. {
  225. p.IsActive = true;
  226. };
  227. SetHandle(element);
  228. // adds
  229. LayoutRoot.Children.Add(element);
  230. particles[name] = p;
  231. // set center
  232. element.SizeChanged += (s, e) =>
  233. {
  234. var size = element.RenderSize;
  235. element.RenderTransform =
  236. new TranslateTransform() { X = -size.Width / 2, Y = -size.Height / 2 };
  237. };
  238. p.IsActive = true;
  239. // returns added the Particle instance
  240. return p;
  241. }
  242. public void Initialize()
  243. {
  244. sim = new Simulation();
  245. particles = new Dictionary<string, Particle>();
  246. springs = new List<Spring>();
  247. LayoutRoot.Children.Clear();
  248. }
  249. /// <summary>
  250. /// Adds a Grid + TextBlock node.
  251. /// </summary>
  252. /// <param name="text"></param>
  253. /// <param name="x"></param>
  254. /// <param name="y"></param>
  255. /// <returns></returns>
  256. public Particle AddNodeGridTextBlock(string text, double x, double y)
  257. {
  258. // creates a new TextBlock instance
  259. var textBlock = new TextBlock()
  260. {
  261. Text = text,
  262. FontSize = 14.0,
  263. //Foreground = new SolidColorBrush(Colors.White)
  264. };
  265. var grid = new Grid()
  266. {
  267. //Background = new SolidColorBrush(Colors.DarkGray),
  268. };
  269. grid.Children.Add(textBlock);
  270. return AddNodeFrameworkElement(grid, text, 10, x, y);
  271. }
  272. public Particle AddNodeBlock(string text, double mass, double x, double y,
  273. double fontSize, string fontColor,
  274. string fill, string stroke, double strokeThickness)
  275. {
  276. // creates a new TextBlock instance
  277. var block = new Block()
  278. {
  279. Text = text,
  280. Fill = new SolidColorBrush(Str2Color(fill)),
  281. Stroke = new SolidColorBrush(Str2Color(stroke)),
  282. StrokeThickness = strokeThickness,
  283. FontSize = fontSize,
  284. Foreground = new SolidColorBrush(Str2Color(fontColor)),
  285. //FontWeight = FontWeights.Bold
  286. };
  287. return AddNodeFrameworkElement(block, text, mass, x, y);
  288. }
  289. /// <summary>
  290. /// Add a Xaml node.
  291. /// </summary>
  292. /// <param name="xaml"></param>
  293. /// <param name="name"></param>
  294. /// <param name="x"></param>
  295. /// <param name="y"></param>
  296. /// <returns></returns>
  297. public Particle AddNodeXaml(string xaml, string name, double x, double y)
  298. {
  299. var element = XamlReader.Load(xaml) as FrameworkElement;
  300. return AddNodeFrameworkElement(element, name, 10, x, y);
  301. }
  302. /// <summary>
  303. /// Deserializes JSON.
  304. /// </summary>
  305. /// <typeparam name="T"></typeparam>
  306. /// <param name="jsonString"></param>
  307. /// <returns></returns>
  308. public static T Deserialize<T>(string jsonString)
  309. {
  310. using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString)))
  311. {
  312. DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(graph));
  313. return (T)ser.ReadObject(ms);
  314. }
  315. }
  316. /// <summary>
  317. /// Load from JSON.
  318. /// </summary>
  319. /// <param name="jsonString"></param>
  320. public void LoadJson(string jsonString)
  321. {
  322. var g = Deserialize<graph>(jsonString);
  323. foreach (var n in g.Nodes)
  324. {
  325. var name = n.Name;
  326. var x = n.X;
  327. var y = n.Y;
  328. AddNodeBlock(name, 10, x, y, 12, "#FFFFFF", "#FF0000", "#FF0000", 2);
  329. }
  330. foreach (var e in g.Edges)
  331. {
  332. Particle s, t;
  333. var source = e.Source;
  334. var target = e.Target;
  335. var Length = e.Length > 0 ? e.Length : 50.0;
  336. if (particles.TryGetValue(source, out s) && particles.TryGetValue(target, out t))
  337. AddEdgeTriangle(s, t, Length, 0.1, 0.1, 2.0, "#777777", "#555555", 0.5);
  338. }
  339. }
  340. /// <summary>
  341. /// Load from XML
  342. /// </summary>
  343. /// <example>
  344. /// var text = @'
  345. /// &lt;Nodes&gt;
  346. /// &lt;Node Name="foo" /&gt;
  347. /// &lt;Node Name="bar" Style="Max" /&gt;
  348. /// &lt;/Nodes&gt;
  349. /// &lt;Links&gt;
  350. /// &lt;Link Source="foo" Target="bar" /&gt;
  351. /// &lt;/Links&gt;';
  352. /// LoadXml(text);
  353. /// </example>
  354. /// <param name="text"></param>
  355. public void LoadXml(string text)
  356. {
  357. var xdoc = XDocument.Parse(text);
  358. var a = 1;
  359. foreach (var node in xdoc.Descendants("Node"))
  360. {
  361. var name = node.Attribute("Name").Value;
  362. var style = node.Attribute("Style") == null ? "More" : node.Element("Style").Value;
  363. var x = 100 + Math.Cos(a);
  364. var y = 100 + Math.Sin(a);
  365. AddNodeGridTextBlock(name, x, y);
  366. a++;
  367. }
  368. foreach (var link in xdoc.Descendants("Link"))
  369. {
  370. var source = link.Attribute("Source").Value;
  371. var target = link.Attribute("Target").Value;
  372. Particle s, t;
  373. if (particles.TryGetValue(source, out s) && particles.TryGetValue(target, out t))
  374. AddEdgeLine(s, t, 100, 0.1, 0.1);
  375. }
  376. }
  377. /// <summary>
  378. /// Sets a bounding box for particles in this simulation.
  379. /// </summary>
  380. /// <param name="width"></param>
  381. /// <param name="height"></param>
  382. public void SetBounds(double width, double height)
  383. {
  384. sim.bounds = new Rect(0, 0, width, height);
  385. }
  386. public void Start()
  387. {
  388. sim.Start();
  389. }
  390. public void Stop()
  391. {
  392. sim.Stop();
  393. }
  394. // Private Methods (7) 
  395. void element_MouseDown(object sender, MouseButtonEventArgs e)
  396. {
  397. FrameworkElement item = sender as FrameworkElement;
  398. mousePosition = e.GetPosition(null);
  399. isMouseCaptured = true;
  400. item.CaptureMouse();
  401. item.Cursor = Cursors.Hand;
  402. e.Handled = true;
  403. }
  404. void element_MouseEnter(object sender, MouseEventArgs e)
  405. {
  406. var item = sender as FrameworkElement;
  407. item.Cursor = Cursors.Hand;
  408. }
  409. void element_MouseLeave(object sender, MouseEventArgs e)
  410. {
  411. var item = sender as FrameworkElement;
  412. item.Cursor = null;
  413. }
  414. void element_MouseMove(object sender, MouseEventArgs e)
  415. {
  416. FrameworkElement item = sender as FrameworkElement;
  417. if (isMouseCaptured)
  418. {
  419. // Calculate the current position of the object.
  420. double deltaV = e.GetPosition(null).Y - mousePosition.Y;
  421. double deltaH = e.GetPosition(null).X - mousePosition.X;
  422. double newTop = deltaV + (double)item.GetValue(Canvas.TopProperty);
  423. double newLeft = deltaH + (double)item.GetValue(Canvas.LeftProperty);
  424. // Set new position of object.
  425. item.SetValue(Canvas.TopProperty, newTop);
  426. item.SetValue(Canvas.LeftProperty, newLeft);
  427. // Update position global variables.
  428. mousePosition = e.GetPosition(null);
  429. }
  430. }
  431. void element_MouseUp(object sender, MouseButtonEventArgs e)
  432. {
  433. FrameworkElement item = sender as FrameworkElement;
  434. isMouseCaptured = false;
  435. item.ReleaseMouseCapture();
  436. mousePosition.X = mousePosition.Y = 0;
  437. item.Cursor = null;
  438. e.Handled = true;
  439. }
  440. void Graph_Loaded(object sender, RoutedEventArgs e)
  441. {
  442. sim.Particles.ForEach(p => p.IsActive = true);
  443. }
  444. void SetHandle(FrameworkElement element)
  445. {
  446. element.MouseLeftButtonDown += new MouseButtonEventHandler(element_MouseDown);
  447. element.MouseMove += new MouseEventHandler(element_MouseMove);
  448. element.MouseLeftButtonUp += new MouseButtonEventHandler(element_MouseUp);
  449. element.MouseEnter += new MouseEventHandler(element_MouseEnter);
  450. element.MouseLeave += new MouseEventHandler(element_MouseLeave);
  451. }
  452. #endregion Methods 
  453. #region Nested Classes (3) 
  454. public class edge
  455. {
  456. #region Properties (5) 
  457. public double Damping { get; set; }
  458. public double Length { get; set; }
  459. public string Source { get; set; }
  460. public string Target { get; set; }
  461. public double Tenstion { get; set; }
  462. #endregion Properties 
  463. }
  464. public class graph
  465. {
  466. #region Fields (2) 
  467. public List<edge> Edges = new List<edge>();
  468. public List<node> Nodes = new List<node>();
  469. #endregion Fields 
  470. }
  471. public class node
  472. {
  473. #region Properties (3) 
  474. public string Name { get; set; }
  475. public double X { get; set; }
  476. public double Y { get; set; }
  477. #endregion Properties 
  478. }
  479. #endregion Nested Classes 
  480. /// <summary>
  481. /// string str = "#ffffff"
  482. /// </summary>
  483. /// <param name="hexaColor"></param>
  484. /// <returns></returns>
  485. public static Color Str2Color(string str)
  486. {
  487. return Color.FromArgb(255,
  488. Convert.ToByte(str.Substring(1, 2), 16),
  489. Convert.ToByte(str.Substring(3, 2), 16),
  490. Convert.ToByte(str.Substring(5, 2), 16));
  491. }
  492. }
  493. }