diff --git a/FractalPainter/App/Program.cs b/FractalPainter/App/Program.cs
index fc0c5bb28..c6aa3c128 100644
--- a/FractalPainter/App/Program.cs
+++ b/FractalPainter/App/Program.cs
@@ -1,6 +1,5 @@
using System;
using System.Windows.Forms;
-using Ninject;
namespace FractalPainting.App
{
diff --git a/FractalPainter/fractalPainter.csproj b/FractalPainter/fractalPainter.csproj
index b30602f95..a6aad5cb9 100644
--- a/FractalPainter/fractalPainter.csproj
+++ b/FractalPainter/fractalPainter.csproj
@@ -36,10 +36,16 @@
FractalPainting.App.Program
+
+ ..\packages\Autofac.4.2.1\lib\net45\Autofac.dll
+
..\packages\Castle.Core.3.2.0\lib\net45\Castle.Core.dll
True
+
+ ..\packages\FakeItEasy.3.0.0-beta002-build000066\lib\net40\FakeItEasy.dll
+
..\packages\FluentAssertions.4.15.0\lib\net45\FluentAssertions.dll
True
diff --git a/FractalPainter/packages.config b/FractalPainter/packages.config
index c4c2a676a..08ed79206 100644
--- a/FractalPainter/packages.config
+++ b/FractalPainter/packages.config
@@ -1,6 +1,8 @@
+
+
diff --git a/TagsCloudApp/TagsCloudApp/Core/CloudCreator.cs b/TagsCloudApp/TagsCloudApp/Core/CloudCreator.cs
new file mode 100644
index 000000000..1af1b8f5d
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Core/CloudCreator.cs
@@ -0,0 +1,31 @@
+using System.Drawing;
+using TagsCloudApp.Core.Interfaces;
+
+namespace TagsCloudApp.Core
+{
+ public class CloudCreator
+ {
+ private readonly ICloudBuilder cloudBuilder;
+ private readonly IFileReader reader;
+ private readonly IPrioritySetter prioritySetter;
+ private readonly ITextPreprocessor textPreprocessor;
+
+ public CloudCreator(ICloudBuilder cloudBuilder, IFileReader reader, IPrioritySetter prioritySetter,
+ ITextPreprocessor textPreprocessor)
+ {
+ this.cloudBuilder = cloudBuilder;
+ this.reader = reader;
+ this.prioritySetter = prioritySetter;
+ this.textPreprocessor = textPreprocessor;
+ }
+
+ public TagCloud Create(TagCloudSettings settings)
+ {
+ var words = reader.GetFileContetByWords(settings.PathToWords);
+ var processedWords = textPreprocessor.ProcessWords(words);
+ var priorities = prioritySetter.SetPriorities(processedWords, settings);
+ var size = settings.Size;
+ return new TagCloud(cloudBuilder.BuildCloud(priorities, new Point(size.Width / 2, size.Height / 2)), size);
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Core/Extensions/PointExtensions.cs b/TagsCloudApp/TagsCloudApp/Core/Extensions/PointExtensions.cs
new file mode 100644
index 000000000..0f9f4c0c8
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Core/Extensions/PointExtensions.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Drawing;
+
+namespace TagsCloudApp.Core.Extensions
+{
+ public static class PointExtensions
+ {
+ public static Point SnapByX(this Point p)
+ {
+ return new Point(p.X / (p.X != 0 ? Math.Abs(p.X) : 1), 0);
+ }
+
+ public static Point SnapByY(this Point p)
+ {
+ return new Point(0, p.Y / (p.Y != 0 ? Math.Abs(p.Y) : 1));
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Core/Extensions/RectangleExtension.cs b/TagsCloudApp/TagsCloudApp/Core/Extensions/RectangleExtension.cs
new file mode 100644
index 000000000..1939d57b9
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Core/Extensions/RectangleExtension.cs
@@ -0,0 +1,9 @@
+using System.Drawing;
+
+namespace TagsCloudApp.Core.Extensions
+{
+ public static class RectangleExtension
+ {
+ public static Point GetCenter(this Rectangle rect) => rect.Location + new Size(rect.Width / 2, rect.Height / 2);
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Core/Interfaces/ICloudBuilder.cs b/TagsCloudApp/TagsCloudApp/Core/Interfaces/ICloudBuilder.cs
new file mode 100644
index 000000000..1af53567d
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Core/Interfaces/ICloudBuilder.cs
@@ -0,0 +1,10 @@
+using System.Collections.Generic;
+using System.Drawing;
+
+namespace TagsCloudApp.Core.Interfaces
+{
+ public interface ICloudBuilder
+ {
+ List BuildCloud(List cloudItems, Point newCenter);
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Core/Interfaces/IFileReader.cs b/TagsCloudApp/TagsCloudApp/Core/Interfaces/IFileReader.cs
new file mode 100644
index 000000000..1a68ede63
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Core/Interfaces/IFileReader.cs
@@ -0,0 +1,9 @@
+using System.Collections.Generic;
+
+namespace TagsCloudApp.Core.Interfaces
+{
+ public interface IFileReader
+ {
+ IEnumerable GetFileContetByWords(string path);
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Core/Interfaces/IPrioritySetter.cs b/TagsCloudApp/TagsCloudApp/Core/Interfaces/IPrioritySetter.cs
new file mode 100644
index 000000000..5436acafa
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Core/Interfaces/IPrioritySetter.cs
@@ -0,0 +1,9 @@
+using System.Collections.Generic;
+
+namespace TagsCloudApp.Core.Interfaces
+{
+ public interface IPrioritySetter
+ {
+ List SetPriorities(IEnumerable words, TagCloudSettings settings);
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Core/Interfaces/IRenderer.cs b/TagsCloudApp/TagsCloudApp/Core/Interfaces/IRenderer.cs
new file mode 100644
index 000000000..6681e2fbd
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Core/Interfaces/IRenderer.cs
@@ -0,0 +1,10 @@
+using System.Drawing;
+
+namespace TagsCloudApp.Core.Interfaces
+{
+ public interface IRenderer
+ {
+ Image RenderImage(TagCloud cloud);
+ void SaveImageTo(string path, Image image);
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Core/Interfaces/ITextPreprocessor.cs b/TagsCloudApp/TagsCloudApp/Core/Interfaces/ITextPreprocessor.cs
new file mode 100644
index 000000000..c5c90a843
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Core/Interfaces/ITextPreprocessor.cs
@@ -0,0 +1,9 @@
+using System.Collections.Generic;
+
+namespace TagsCloudApp.Core.Interfaces
+{
+ public interface ITextPreprocessor
+ {
+ List ProcessWords(IEnumerable words);
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Core/Interfaces/IVizualizer.cs b/TagsCloudApp/TagsCloudApp/Core/Interfaces/IVizualizer.cs
new file mode 100644
index 000000000..901c1536f
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Core/Interfaces/IVizualizer.cs
@@ -0,0 +1,7 @@
+namespace TagsCloudApp.Core.Interfaces
+{
+ public interface IVizualizer
+ {
+ void RunVizualizer();
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Core/TagCloud.cs b/TagsCloudApp/TagsCloudApp/Core/TagCloud.cs
new file mode 100644
index 000000000..39bc6bf8e
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Core/TagCloud.cs
@@ -0,0 +1,18 @@
+using System.Collections.Generic;
+using System.Drawing;
+
+namespace TagsCloudApp.Core
+{
+ public class TagCloud
+ {
+ private readonly List items;
+ public Size Size { get; }
+ public IEnumerable Items => items.AsReadOnly();
+
+ public TagCloud(List items, Size size)
+ {
+ this.items = items;
+ Size = size;
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Core/TagCloudItem.cs b/TagsCloudApp/TagsCloudApp/Core/TagCloudItem.cs
new file mode 100644
index 000000000..01a680000
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Core/TagCloudItem.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Drawing;
+
+namespace TagsCloudApp.Core
+{
+ public class TagCloudItem
+ {
+ public readonly WordInfo WordInfo;
+ public readonly Color Color;
+ public readonly Font Font;
+ public readonly Rectangle Rectangle;
+
+ public TagCloudItem(Color color, WordInfo wordInfo, Font font, Rectangle rectangle)
+ {
+ Color = color;
+ WordInfo = wordInfo;
+ Font = font;
+ Rectangle = rectangle;
+ }
+
+ public TagCloudItem SetRectangle(Rectangle newRectangle) => new TagCloudItem(Color, WordInfo, Font, newRectangle);
+
+ public static TagCloudItem GetRandomFontSize(Random rnd)
+ {
+ return new TagCloudItem(Color.AliceBlue, new WordInfo("", 100), new Font("Menlo", rnd.Next(int.MaxValue)),
+ default(Rectangle));
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Core/TagCloudSettings.cs b/TagsCloudApp/TagsCloudApp/Core/TagCloudSettings.cs
new file mode 100644
index 000000000..8afe44d0d
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Core/TagCloudSettings.cs
@@ -0,0 +1,25 @@
+using System.Drawing;
+
+namespace TagsCloudApp.Core
+{
+ public class TagCloudSettings
+ {
+ public static readonly TagCloudSettings DefaultSettings =
+ new TagCloudSettings(new Font("Meslo", 10), Color.GreenYellow, "test.txt", "test", new Size(500, 500));
+
+ public Font Font { get; set; }
+ public Color Color { get; set; }
+ public string PathToWords { get; set; }
+ public string PathToSave { get; set; }
+ public Size Size { get; set; }
+
+ public TagCloudSettings(Font font, Color color, string pathToWords, string pathToSave, Size size)
+ {
+ Font = font;
+ Color = color;
+ PathToWords = pathToWords;
+ PathToSave = pathToSave;
+ Size = size;
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Core/WordInfo.cs b/TagsCloudApp/TagsCloudApp/Core/WordInfo.cs
new file mode 100644
index 000000000..a4604d107
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Core/WordInfo.cs
@@ -0,0 +1,41 @@
+namespace TagsCloudApp.Core
+{
+ public class WordInfo
+ {
+ public readonly string Word;
+ public readonly double Frequency;
+
+ public WordInfo(string word, double frequency)
+ {
+ Word = word;
+ Frequency = frequency;
+ }
+
+ private bool Equals(WordInfo other)
+ {
+ return string.Equals(Word, other.Word);
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj)) return false;
+ if (ReferenceEquals(this, obj)) return true;
+ return obj.GetType() == GetType() && Equals((WordInfo) obj);
+ }
+
+ public override int GetHashCode()
+ {
+ return Word?.GetHashCode() ?? 0;
+ }
+
+ public static bool operator ==(WordInfo left, WordInfo right)
+ {
+ return Equals(left, right);
+ }
+
+ public static bool operator !=(WordInfo left, WordInfo right)
+ {
+ return !Equals(left, right);
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Implementations/CircularCloudBuilder.cs b/TagsCloudApp/TagsCloudApp/Implementations/CircularCloudBuilder.cs
new file mode 100644
index 000000000..0acc9b178
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Implementations/CircularCloudBuilder.cs
@@ -0,0 +1,121 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using TagsCloudApp.Core;
+using TagsCloudApp.Core.Extensions;
+using TagsCloudApp.Core.Interfaces;
+
+namespace TagsCloudApp.Implementations
+{
+ public class CircularCloudBuilder : ICloudBuilder
+ {
+ private Point center;
+ private Spiral spiral;
+ private Rectangle cloudBorders;
+
+ private List PlacedRectangles { get; set; }
+
+ private Point Center
+ {
+ set
+ {
+ if (value.X < 0 || value.Y < 0)
+ throw new ArgumentException("Center point should be non-negative");
+ center = value;
+ }
+ }
+
+ public CircularCloudBuilder()
+ {
+ PlacedRectangles = new List();
+ }
+
+ private void InitCloud(Point newCenter)
+ {
+ Center = newCenter;
+ spiral = new Spiral(newCenter);
+ cloudBorders = new Rectangle(0, 0, newCenter.X * 2, newCenter.Y * 2);
+ PlacedRectangles = new List();
+ }
+
+ public List BuildCloud(List cloudItems, Point newCenter)
+ {
+ InitCloud(newCenter);
+
+ var placedItems = new List();
+ foreach (var cloudItem in cloudItems)
+ try
+ {
+ placedItems.Add(cloudItem.SetRectangle(PutNextRectangle(cloudItem.Rectangle.Size)));
+ }
+ catch (ArgumentException)
+ {
+ break;
+ }
+ return placedItems;
+ }
+
+ private Rectangle PutNextRectangle(Size rectangleSize)
+ {
+ if (rectangleSize.Height <= 0 || rectangleSize.Width <= 0)
+ throw new ArgumentException($"Size must be positive {rectangleSize}");
+
+ var nextRectangle = FindNextRectanglePosition(rectangleSize);
+ if (nextRectangle.IsEmpty) return nextRectangle;
+
+ nextRectangle = MoveToCenter(nextRectangle);
+ PlacedRectangles.Add(nextRectangle);
+ return nextRectangle;
+ }
+
+ private Rectangle FindNextRectanglePosition(Size rectangleSize)
+ {
+ var nextRectangle = new Rectangle(GetRectangleCenterLocation(rectangleSize, spiral.GetNextSpiralPoint()),
+ rectangleSize);
+
+ while (!IsInValidPosition(nextRectangle))
+ {
+ var nextSpiralPoint = spiral.GetNextSpiralPoint();
+ nextRectangle = new Rectangle(GetRectangleCenterLocation(rectangleSize, nextSpiralPoint), rectangleSize);
+ if (!cloudBorders.Contains(nextSpiralPoint))
+ throw new ArgumentException("Can't place rectangle because cloud is too small");
+ }
+
+ return nextRectangle;
+ }
+
+ private static Point GetRectangleCenterLocation(Size rectangleSize, Point nextSpiralPoint)
+ {
+ return new Point(nextSpiralPoint.X - rectangleSize.Width / 2, nextSpiralPoint.Y - rectangleSize.Height / 2);
+ }
+
+ private bool IsInValidPosition(Rectangle checkingRectangle)
+ {
+ return !PlacedRectangles.Any(rect => rect.IntersectsWith(checkingRectangle)) &&
+ cloudBorders.Contains(checkingRectangle);
+ }
+
+ private Rectangle MoveToCenter(Rectangle rectangle)
+ {
+ var newRectangle = Rectangle.Empty;
+ while (rectangle != newRectangle)
+ {
+ if (!newRectangle.IsEmpty)
+ rectangle = newRectangle;
+ var vectorToCenter = center - new Size(rectangle.GetCenter());
+ newRectangle = TryMove(rectangle, vectorToCenter.SnapByX());
+ newRectangle = TryMove(newRectangle, vectorToCenter.SnapByY());
+ }
+ return rectangle;
+ }
+
+ private Rectangle TryMove(Rectangle rectangle, Point shift)
+ {
+ var newRect = new Rectangle(rectangle.Location + new Size(shift), rectangle.Size);
+ if (IsInValidPosition(newRect))
+ rectangle = newRect;
+ return rectangle;
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Implementations/ConsoleVizualizer.cs b/TagsCloudApp/TagsCloudApp/Implementations/ConsoleVizualizer.cs
new file mode 100644
index 000000000..8ce093ee8
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Implementations/ConsoleVizualizer.cs
@@ -0,0 +1,40 @@
+using System;
+using TagsCloudApp.Core;
+using TagsCloudApp.Core.Interfaces;
+
+namespace TagsCloudApp.Implementations
+{
+ public class ConsoleVizualizer : IVizualizer
+ {
+ private readonly CloudCreator creator;
+ private readonly TagCloudSettings settings;
+ private string cmd;
+
+ public ConsoleVizualizer(CloudCreator creator, TagCloudSettings settings)
+ {
+ this.creator = creator;
+ this.settings = settings;
+ cmd = "";
+ }
+
+ private void DrawCloud()
+ {
+ var cloud = creator.Create(settings);
+ foreach (var cloudItem in cloud.Items)
+ Console.WriteLine($"\"{cloudItem.WordInfo.Word}\" count = {cloudItem.WordInfo.Frequency}");
+ }
+
+ public void RunVizualizer()
+ {
+ while (cmd != "exit")
+ {
+ Console.Clear();
+ if (cmd == "draw")
+ DrawCloud();
+ else if (cmd == "help")
+ Console.WriteLine("Type 'draw' to draw cloud");
+ cmd = Console.ReadLine();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Implementations/Gui/SettingsForm.cs b/TagsCloudApp/TagsCloudApp/Implementations/Gui/SettingsForm.cs
new file mode 100644
index 000000000..8cf2b6b35
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Implementations/Gui/SettingsForm.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Windows.Forms;
+
+namespace TagsCloudApp.Implementations.Gui
+{
+ public class SettingsForm : Form
+ {
+ public SettingsForm(TSettings settings)
+ {
+ var okButton = new Button
+ {
+ Text = "OK",
+ DialogResult = DialogResult.OK,
+ Dock = DockStyle.Bottom,
+ };
+ Controls.Add(okButton);
+ Controls.Add(new PropertyGrid
+ {
+ SelectedObject = settings,
+ Dock = DockStyle.Fill
+ });
+ AcceptButton = okButton;
+ }
+
+ protected override void OnLoad(EventArgs e)
+ {
+ base.OnLoad(e);
+ Text = "Settings";
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Implementations/Gui/WinFormVizualizer.cs b/TagsCloudApp/TagsCloudApp/Implementations/Gui/WinFormVizualizer.cs
new file mode 100644
index 000000000..2e321e311
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Implementations/Gui/WinFormVizualizer.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Drawing;
+using System.Windows.Forms;
+using TagsCloudApp.Core;
+using TagsCloudApp.Core.Interfaces;
+
+namespace TagsCloudApp.Implementations.Gui
+{
+ public class WinFormVizualizer : IVizualizer
+ {
+ private readonly CloudCreator creator;
+ private readonly IRenderer renderer;
+ private readonly TagCloudSettings settings;
+ private Image image;
+ private readonly Form form;
+
+ public WinFormVizualizer(CloudCreator creator, IRenderer renderer, TagCloudSettings settings)
+ {
+ this.creator = creator;
+ this.renderer = renderer;
+ this.settings = settings;
+ form = new Form();
+ InitForm();
+ MessageBox.Show("To change settings press 'n'\nTo save cloud press 's'");
+ DrawCloud();
+ }
+
+ private void InitForm()
+ {
+ form.KeyPress += (o, e) =>
+ {
+ if (e.KeyChar == 'n')
+ {
+ new SettingsForm(settings).ShowDialog();
+ DrawCloud();
+ }
+ else if (e.KeyChar == 's')
+ renderer.SaveImageTo(settings.PathToSave, image);
+ };
+ form.Paint += (o, e) => {
+ e.Graphics.DrawImage(image, new Point(0, 0));
+ form.Size = image.Size;
+ };
+ }
+
+ private void DrawCloud()
+ {
+ try
+ {
+ image = renderer.RenderImage(creator.Create(settings));
+ form.Invalidate();
+ form.Show();
+ }
+ catch (ArgumentException ex)
+ {
+ MessageBox.Show(ex.Message);
+ }
+ }
+
+ public void RunVizualizer()
+ {
+ Application.Run(form);
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Implementations/LogarithmicPrioritySetter.cs b/TagsCloudApp/TagsCloudApp/Implementations/LogarithmicPrioritySetter.cs
new file mode 100644
index 000000000..dcff922f1
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Implementations/LogarithmicPrioritySetter.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using TagsCloudApp.Core;
+using TagsCloudApp.Core.Interfaces;
+
+namespace TagsCloudApp.Implementations
+{
+ public class SamePrioritySetter : IPrioritySetter
+ {
+ public List SetPriorities(IEnumerable words, TagCloudSettings settings)
+ {
+ var wordInfos = new HashSet(words);
+ var g = Graphics.FromImage(new Bitmap(1, 1));
+
+ var minFontSize = settings.Font.Size;
+ const int offset = 10;
+ return wordInfos
+ .Select(word =>
+ {
+ var font = new Font(settings.Font.FontFamily, (float) (minFontSize * Math.Max(1, Math.Log(word.Frequency))));
+ var size = g.MeasureString(word.Word, font).ToSize();
+ return new TagCloudItem(settings.Color, word,
+ font, new Rectangle(0, 0, size.Width + offset, size.Height + offset));
+ })
+ .ToList();
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Implementations/PngRenderer.cs b/TagsCloudApp/TagsCloudApp/Implementations/PngRenderer.cs
new file mode 100644
index 000000000..94d647c13
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Implementations/PngRenderer.cs
@@ -0,0 +1,33 @@
+using System.Drawing;
+using TagsCloudApp.Core;
+using TagsCloudApp.Core.Interfaces;
+
+namespace TagsCloudApp.Implementations
+{
+ public class PngRenderer : IRenderer
+ {
+ public Image RenderImage(TagCloud cloud)
+ {
+ var image = new Bitmap(cloud.Size.Width, cloud.Size.Height);
+ var g = Graphics.FromImage(image);
+ g.FillRectangle(Brushes.Black, new Rectangle(new Point(0, 0), cloud.Size));
+
+ foreach (var cloudItem in cloud.Items)
+ g.DrawString(cloudItem);
+ return image;
+ }
+
+ public void SaveImageTo(string path, Image image)
+ {
+ image.Save(path + ".png");
+ }
+ }
+
+ internal static class GraphicsExtension
+ {
+ public static void DrawString(this Graphics g, TagCloudItem item)
+ {
+ g.DrawString(item.WordInfo.Word, item.Font, new SolidBrush(item.Color), item.Rectangle);
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Implementations/SimpleTextPreprocessor.cs b/TagsCloudApp/TagsCloudApp/Implementations/SimpleTextPreprocessor.cs
new file mode 100644
index 000000000..196c9104e
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Implementations/SimpleTextPreprocessor.cs
@@ -0,0 +1,104 @@
+using System.Collections.Generic;
+using System.Linq;
+using TagsCloudApp.Core;
+using TagsCloudApp.Core.Interfaces;
+
+namespace TagsCloudApp.Implementations
+{
+ public class SimpleTextPreprocessor : ITextPreprocessor
+ {
+ private static readonly HashSet StopWords = new HashSet
+ {
+ "he",
+ "she",
+ "and",
+ "it",
+ "aboard",
+ "about",
+ "above",
+ "across",
+ "after",
+ "against",
+ "along",
+ "amid",
+ "among",
+ "anti",
+ "around",
+ "as",
+ "at",
+ "before",
+ "behind",
+ "below",
+ "beneath",
+ "beside",
+ "besides",
+ "between",
+ "beyond",
+ "but",
+ "by",
+ "concerning",
+ "considering",
+ "despite",
+ "down",
+ "during",
+ "except",
+ "excepting",
+ "excluding",
+ "following",
+ "for",
+ "from",
+ "in",
+ "inside",
+ "into",
+ "like",
+ "minus",
+ "near",
+ "of",
+ "off",
+ "on",
+ "onto",
+ "opposite",
+ "outside",
+ "over",
+ "past",
+ "per",
+ "plus",
+ "regarding",
+ "round",
+ "save",
+ "since",
+ "than",
+ "the",
+ "through",
+ "to",
+ "toward",
+ "towards",
+ "under",
+ "underneath",
+ "unlike",
+ "until",
+ "up",
+ "upon",
+ "versus",
+ "via",
+ "with",
+ "within",
+ "without"
+ };
+
+ public List ProcessWords(IEnumerable words)
+ {
+ return words.Select(w => w.ToLowerInvariant())
+ .Where(CheckWord)
+ .GroupBy(word => word)
+ .Select(kv => new WordInfo(kv.Key, kv.Count()))
+ .ToList();
+ }
+
+ private static bool CheckWord(string word)
+ {
+ return !(StopWords.Contains(word) || word.Length < 2 ||
+ string.IsNullOrWhiteSpace(word) || string.IsNullOrEmpty(word));
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Implementations/Spiral.cs b/TagsCloudApp/TagsCloudApp/Implementations/Spiral.cs
new file mode 100644
index 000000000..fd404ede6
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Implementations/Spiral.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Drawing;
+
+namespace TagsCloudApp.Implementations
+{
+ public class Spiral
+ {
+ private readonly Point center;
+ private readonly double deltaAngle;
+ private readonly double deltaRadius;
+ private Point currentPoint;
+
+ public double CurrentAngle { get; set; }
+
+ public double CurrentRadius { get; set; }
+
+ public Spiral(Point center, double deltaAngle = Math.PI / 180, double deltaRadius = 0.001)
+ {
+ this.center = center;
+ this.deltaAngle = deltaAngle;
+ this.deltaRadius = deltaRadius;
+ currentPoint = this.center;
+ CurrentAngle = 0;
+ CurrentRadius = 0;
+ }
+
+ public Point GetNextSpiralPoint()
+ {
+ var prevPoint = currentPoint;
+ CurrentAngle += deltaAngle;
+ CurrentRadius += deltaRadius;
+
+ var newX = (int) (CurrentRadius * Math.Cos(CurrentAngle) + center.X);
+ var newY = (int) (CurrentRadius * Math.Sin(CurrentAngle) + center.Y);
+ currentPoint = new Point(newX, newY);
+ return prevPoint;
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Implementations/TxtReader.cs b/TagsCloudApp/TagsCloudApp/Implementations/TxtReader.cs
new file mode 100644
index 000000000..8b45701d7
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Implementations/TxtReader.cs
@@ -0,0 +1,14 @@
+using System.Collections.Generic;
+using System.IO;
+using TagsCloudApp.Core.Interfaces;
+
+namespace TagsCloudApp.Implementations
+{
+ public class TxtReader : IFileReader
+ {
+ public IEnumerable GetFileContetByWords(string path)
+ {
+ return File.ReadAllLines(path);
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Program.cs b/TagsCloudApp/TagsCloudApp/Program.cs
new file mode 100644
index 000000000..cf7a40650
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Program.cs
@@ -0,0 +1,25 @@
+using System.Reflection;
+using Autofac;
+using TagsCloudApp.Core;
+using TagsCloudApp.Core.Interfaces;
+
+namespace TagsCloudApp
+{
+ internal static class Program
+ {
+ public static void Main()
+ {
+ var builder = new ContainerBuilder();
+ var asm = Assembly.GetExecutingAssembly();
+ builder.RegisterAssemblyTypes(asm)
+ .AsImplementedInterfaces();
+ builder.RegisterType().AsSelf();
+ builder.RegisterInstance(TagCloudSettings.DefaultSettings).As().SingleInstance();
+
+ var container = builder.Build();
+
+ using (var scope = container.BeginLifetimeScope())
+ scope.Resolve().RunVizualizer();
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Properties/AssemblyInfo.cs b/TagsCloudApp/TagsCloudApp/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..fa1d8d4ec
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Properties/AssemblyInfo.cs
@@ -0,0 +1,39 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+
+[assembly: AssemblyTitle("TagsCloudApp")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("TagsCloudApp")]
+[assembly: AssemblyCopyright("Copyright © 2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+
+[assembly: Guid("32340528-0E6D-4CA1-8048-545865F527B1")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/TagsCloudApp.csproj b/TagsCloudApp/TagsCloudApp/TagsCloudApp.csproj
new file mode 100644
index 000000000..510afd2db
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/TagsCloudApp.csproj
@@ -0,0 +1,108 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {32340528-0E6D-4CA1-8048-545865F527B1}
+ {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ Exe
+ Properties
+ TagsCloudApp
+ TagsCloudApp
+ v4.5
+ 512
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\..\packages\Autofac.4.2.1\lib\net45\Autofac.dll
+
+
+ ..\..\packages\FakeItEasy.3.0.0-beta002-build000066\lib\net40\FakeItEasy.dll
+
+
+ ..\..\packages\FluentAssertions.4.18.0\lib\net45\FluentAssertions.dll
+
+
+ ..\..\packages\FluentAssertions.4.18.0\lib\net45\FluentAssertions.Core.dll
+
+
+ ..\..\packages\NUnit.3.5.0\lib\net45\nunit.framework.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Test/CircularCloudLayouter_Should.cs b/TagsCloudApp/TagsCloudApp/Test/CircularCloudLayouter_Should.cs
new file mode 100644
index 000000000..d513313aa
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Test/CircularCloudLayouter_Should.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Linq;
+using FluentAssertions;
+using NUnit.Framework;
+using TagsCloudApp.Core;
+using TagsCloudApp.Implementations;
+
+namespace TagsCloudApp.Test
+{
+ [TestFixture]
+ internal class CircularCloudLayouter_Should
+ {
+ [Test]
+ public void PlaceRectWOIntersection_WhenBuildCloudCalls()
+ {
+ var cloud = new CircularCloudBuilder();
+
+ var placed = cloud.BuildCloud(GenerateRectangleSizes(10, 1).ToList(), new Point(1000, 1000));
+
+ placed
+ .SelectMany(r1 => placed,
+ (r2, r1) => r1.Rectangle.IntersectsWith(r2.Rectangle))
+ .Any(r => r)
+ .Should().BeFalse();
+ }
+
+
+ private static IEnumerable GenerateRectangleSizes(int numberOfRectangles, int seed)
+ {
+ var rand = new Random(seed);
+ return Enumerable.Range(0, numberOfRectangles)
+ .Select(i => TagCloudItem.GetRandomFontSize(rand));
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Test/CloudCreator_Should.cs b/TagsCloudApp/TagsCloudApp/Test/CloudCreator_Should.cs
new file mode 100644
index 000000000..8bbbda7ff
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Test/CloudCreator_Should.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using FakeItEasy;
+using NUnit.Framework;
+using TagsCloudApp.Core;
+using TagsCloudApp.Core.Interfaces;
+
+namespace TagsCloudApp.Test
+{
+ [TestFixture]
+ public class CloudCreator_Should
+ {
+ [Test]
+ public void DoSomething_WhenSomething()
+ {
+ var builder = A.Fake();
+ var reader = A.Fake();
+ var prioritySetter = A.Fake();
+ var textPreprocessor = A.Fake();
+ var creator = new CloudCreator(builder, reader, prioritySetter, textPreprocessor);
+ var random = new Random(1);
+
+ A.CallTo(() => reader.GetFileContetByWords(null))
+ .WithAnyArguments()
+ .Returns(new List {"one", "two", "three"});
+ A.CallTo(() => textPreprocessor.ProcessWords(null))
+ .WithAnyArguments()
+ .Returns(new List {new WordInfo("one", 1), new WordInfo("two", 1)});
+ A.CallTo(() => prioritySetter.SetPriorities(null, null))
+ .WithAnyArguments()
+ .Returns(new List {TagCloudItem.GetRandomFontSize(random)});
+ A.CallTo(() => builder.BuildCloud(null, default(Point)))
+ .WithAnyArguments()
+ .Returns(new List {TagCloudItem.GetRandomFontSize(random)});
+
+ creator.Create(TagCloudSettings.DefaultSettings);
+
+ A.CallTo(() => reader.GetFileContetByWords(null))
+ .WithAnyArguments()
+ .MustHaveHappened(Repeated.Exactly.Once);
+ A.CallTo(() => textPreprocessor.ProcessWords(null))
+ .WithAnyArguments()
+ .MustHaveHappened(Repeated.Exactly.Once);
+ A.CallTo(() => prioritySetter.SetPriorities(null, null))
+ .WithAnyArguments()
+ .MustHaveHappened(Repeated.Exactly.Once);
+ A.CallTo(() => builder.BuildCloud(null, default(Point)))
+ .WithAnyArguments()
+ .MustHaveHappened(Repeated.Exactly.Once);
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Test/FunctionalTest.cs b/TagsCloudApp/TagsCloudApp/Test/FunctionalTest.cs
new file mode 100644
index 000000000..8c4f591b2
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Test/FunctionalTest.cs
@@ -0,0 +1,29 @@
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using FluentAssertions;
+using NUnit.Framework;
+using TagsCloudApp.Core;
+using TagsCloudApp.Implementations;
+
+namespace TagsCloudApp.Test
+{
+ [TestFixture]
+ public class FunctionalTest
+ {
+ [Test]
+ public void TestCloudCreation()
+ {
+ var creator = new CloudCreator(new CircularCloudBuilder(), new TxtReader(), new SamePrioritySetter(),
+ new SimpleTextPreprocessor());
+ var tagCloudSettings = new TagCloudSettings(new Font("Arial", 5), Color.Beige,
+ Path.Combine(TestContext.CurrentContext.TestDirectory, "Test", "test.txt"),
+ "for_test", new Size(1000, 1000));
+
+ var cloud = creator.Create(tagCloudSettings);
+
+ cloud.Items.Any().Should().BeTrue();
+ cloud.Size.Should().Be(tagCloudSettings.Size);
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Test/SimpleTextPreprocessor_Should.cs b/TagsCloudApp/TagsCloudApp/Test/SimpleTextPreprocessor_Should.cs
new file mode 100644
index 000000000..012a0107f
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Test/SimpleTextPreprocessor_Should.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using FluentAssertions;
+using NUnit.Framework;
+using TagsCloudApp.Implementations;
+
+namespace TagsCloudApp.Test
+{
+ [TestFixture]
+ public class SimpleTextPreprocessor_Should
+ {
+ private SimpleTextPreprocessor proc;
+
+ [SetUp]
+ public void SetUp()
+ {
+ proc = new SimpleTextPreprocessor();
+ }
+
+ [Test]
+ public void RemoveStopWords()
+ {
+ var words = new List {"He", "was", "shocked", "off"};
+
+ var processed = proc.ProcessWords(words);
+
+ processed.Select(w => w.Word).Should().NotContain("He");
+ }
+
+ [Test]
+ public void CalculateWordCount()
+ {
+ var words = new List {"He", "was", "He", "was", "He", "He", "He"};
+
+ var processed = proc.ProcessWords(words);
+
+ Console.WriteLine(1);
+ processed.First(w => w.Word == "was").Frequency.Should().Be(2);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Test/Spiral_Should.cs b/TagsCloudApp/TagsCloudApp/Test/Spiral_Should.cs
new file mode 100644
index 000000000..d1bee50e2
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Test/Spiral_Should.cs
@@ -0,0 +1,24 @@
+using System.Drawing;
+using FluentAssertions;
+using NUnit.Framework;
+using TagsCloudApp.Implementations;
+
+namespace TagsCloudApp.Test
+{
+ [TestFixture]
+ public class Spiral_Should
+ {
+ [Test]
+ public void IncrementAngleAndRadiusByDelta_OnNextStep()
+ {
+ const int deltaAngle = 1;
+ const int deltaRadius = 2;
+ var spiral = new Spiral(new Point(100, 100), deltaAngle, deltaRadius);
+ var previousAngle = spiral.CurrentAngle;
+ var previousRadius = spiral.CurrentRadius;
+ spiral.GetNextSpiralPoint();
+ (spiral.CurrentAngle - previousAngle).Should().Be(deltaAngle);
+ (spiral.CurrentRadius - previousRadius).Should().Be(deltaRadius);
+ }
+ }
+}
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/Test/test.txt b/TagsCloudApp/TagsCloudApp/Test/test.txt
new file mode 100644
index 000000000..dbf9ceb7c
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/Test/test.txt
@@ -0,0 +1,1716 @@
+Twenty
+miles
+west
+of
+Tucson
+the
+Sunset
+Express
+stopped
+at
+a
+tank
+to
+take
+on
+water
+Besides
+the
+aqueous
+addition
+the
+engine
+of
+that
+famous
+flyer
+acquired
+some
+other
+things
+that
+were
+not
+good
+for
+it
+While
+the
+fireman
+was
+lowering
+the
+feeding
+hose
+Bob
+Tidball
+Shark
+Dodson
+and
+a
+quarterbred
+Creek
+Indian
+called
+John
+Big
+Dog
+climbed
+on
+the
+engine
+and
+showed
+the
+engineer
+three
+round
+orifices
+in
+pieces
+of
+ordnance
+that
+they
+carried
+These
+orifices
+so
+impressed
+the
+engineer
+with
+their
+possibilities
+that
+he
+raised
+both
+hands
+in
+a
+gesture
+such
+as
+accompanies
+the
+ejaculation
+Do
+tell
+At
+the
+crisp
+command
+of
+Shark
+Dodson
+who
+was
+leader
+of
+the
+attacking
+force
+the
+engineer
+descended
+to
+the
+ground
+and
+uncoupled
+the
+engine
+and
+tender
+Then
+John
+Big
+Dog
+perched
+upon
+the
+coal
+sportively
+held
+two
+guns
+upon
+the
+engine
+driver
+and
+the
+fireman
+and
+suggested
+that
+they
+run
+the
+engine
+fifty
+yards
+away
+and
+there
+await
+further
+orders
+Shark
+Dodson
+and
+Bob
+Tidball
+scorning
+to
+put
+such
+lowgrade
+ore
+as
+the
+passengers
+through
+the
+mill
+struck
+out
+for
+the
+rich
+pocket
+of
+the
+express
+car
+They
+found
+the
+messenger
+serene
+in
+the
+belief
+that
+the
+Sunset
+Express
+was
+taking
+on
+nothing
+more
+stimulating
+and
+dangerous
+than
+aqua
+pura
+While
+Bob
+was
+knocking
+this
+idea
+out
+of
+his
+head
+with
+the
+buttend
+of
+his
+sixshooter
+Shark
+Dodson
+was
+already
+dosing
+the
+expresscar
+safe
+with
+dynamite
+The
+safe
+exploded
+to
+the
+tune
+of
+30000
+all
+gold
+and
+currency
+The
+passengers
+thrust
+their
+heads
+casually
+out
+of
+the
+windows
+to
+look
+for
+the
+thundercloud
+The
+conductor
+jerked
+at
+the
+bellrope
+which
+sagged
+down
+loose
+and
+unresisting
+at
+his
+tug
+Shark
+Dodson
+and
+Bob
+Tidball
+with
+their
+booty
+in
+a
+stout
+canvas
+bag
+tumbled
+out
+of
+the
+express
+car
+and
+ran
+awkwardly
+in
+their
+highheeled
+boots
+to
+the
+engine
+The
+engineer
+sullenly
+angry
+but
+wise
+ran
+the
+engine
+according
+to
+orders
+rapidly
+away
+from
+the
+inert
+train
+But
+before
+this
+was
+accomplished
+the
+express
+messenger
+recovered
+from
+Bob
+Tidballs
+persuader
+to
+neutrality
+jumped
+out
+of
+his
+car
+with
+a
+Winchester
+rifle
+and
+took
+a
+trick
+in
+the
+game
+Mr
+John
+Big
+Dog
+sitting
+on
+the
+coal
+tender
+unwittingly
+made
+a
+wrong
+lead
+by
+giving
+an
+imitation
+of
+a
+target
+and
+the
+messenger
+trumped
+him
+With
+a
+ball
+exactly
+between
+his
+shoulder
+blades
+the
+Creek
+chevalier
+of
+industry
+rolled
+off
+to
+the
+ground
+thus
+increasing
+the
+share
+of
+his
+comrades
+in
+the
+loot
+by
+onesixth
+each
+Two
+miles
+from
+the
+tank
+the
+engineer
+was
+ordered
+to
+stop
+The
+robbers
+waved
+a
+defiant
+adieu
+and
+plunged
+down
+the
+steep
+slope
+into
+the
+thick
+woods
+that
+lined
+the
+track
+Five
+minutes
+of
+crashing
+through
+a
+thicket
+of
+chaparral
+brought
+them
+to
+open
+woods
+where
+three
+horses
+were
+tied
+to
+lowhanging
+branches
+One
+was
+waiting
+for
+John
+Big
+Dog
+who
+would
+never
+ride
+by
+night
+or
+day
+again
+This
+animal
+the
+robbers
+divested
+of
+saddle
+and
+bridle
+and
+set
+free
+They
+mounted
+the
+other
+two
+with
+the
+bag
+across
+one
+pommel
+and
+rode
+fast
+and
+with
+discretion
+through
+the
+forest
+and
+up
+a
+primeval
+lonely
+gorge
+Here
+the
+animal
+that
+bore
+Bob
+Tidball
+slipped
+on
+a
+mossy
+boulder
+and
+broke
+a
+foreleg
+They
+shot
+him
+through
+the
+head
+at
+once
+and
+sat
+down
+to
+hold
+a
+council
+of
+flight
+Made
+secure
+for
+the
+present
+by
+the
+tortuous
+trail
+they
+had
+travelled
+the
+question
+of
+time
+was
+no
+longer
+so
+big
+Many
+miles
+and
+hours
+lay
+between
+them
+and
+the
+spryest
+posse
+that
+could
+follow
+Shark
+Dodsons
+horse
+with
+trailing
+rope
+and
+dropped
+bridle
+panted
+and
+cropped
+thankfully
+of
+the
+grass
+along
+the
+stream
+in
+the
+gorge
+Bob
+Tidball
+opened
+the
+sack
+drew
+out
+double
+handfuls
+of
+the
+neat
+packages
+of
+currency
+and
+the
+one
+sack
+of
+gold
+and
+chuckled
+with
+the
+glee
+of
+a
+child
+Say
+you
+old
+doubledecked
+pirate
+he
+called
+joyfully
+to
+Dodson
+you
+said
+we
+could
+do
+it
+you
+got
+a
+head
+for
+financing
+that
+knocks
+the
+horns
+off
+of
+anything
+in
+Arizona
+What
+are
+we
+going
+to
+do
+about
+a
+hoss
+for
+you
+Bob
+We
+aint
+got
+long
+to
+wait
+here
+Theyll
+be
+on
+our
+trail
+before
+daylight
+in
+the
+mornin
+Oh
+I
+guess
+that
+cayuse
+of
+yournll
+carry
+double
+for
+a
+while
+answered
+the
+sanguine
+Bob
+Well
+annex
+the
+first
+animal
+we
+come
+across
+By
+jingoes
+we
+made
+a
+haul
+didnt
+we
+Accordin
+to
+the
+marks
+on
+this
+money
+theres
+30000
+15000
+apiece
+Its
+short
+of
+what
+I
+expected
+said
+Shark
+Dodson
+kicking
+softly
+at
+the
+packages
+with
+the
+toe
+of
+his
+boot
+And
+then
+he
+looked
+pensively
+at
+the
+wet
+sides
+of
+his
+tired
+horse
+Old
+Bolivars
+mighty
+nigh
+played
+out
+he
+said
+slowly
+I
+wish
+that
+sorrel
+of
+yours
+hadnt
+got
+hurt
+So
+do
+I
+said
+Bob
+heartily
+but
+it
+cant
+be
+helped
+Bolivars
+got
+plenty
+of
+bottom
+hell
+get
+us
+both
+far
+enough
+to
+get
+fresh
+mounts
+Dang
+it
+Shark
+I
+cant
+help
+thinkin
+how
+funny
+it
+is
+that
+an
+Easterner
+like
+you
+can
+come
+out
+here
+and
+give
+us
+Western
+fellows
+cards
+and
+spades
+in
+the
+desperado
+business
+What
+part
+of
+the
+East
+was
+you
+from
+anyway
+New
+York
+State
+said
+Shark
+Dodson
+sitting
+down
+on
+a
+boulder
+and
+chewing
+a
+twig
+I
+was
+born
+on
+a
+farm
+in
+Ulster
+County
+I
+ran
+away
+from
+home
+when
+I
+was
+seventeen
+It
+was
+an
+accident
+my
+coming
+West
+I
+was
+walkin
+along
+the
+road
+with
+my
+clothes
+in
+a
+bundle
+makin
+for
+New
+York
+City
+I
+had
+an
+idea
+of
+goin
+there
+and
+makin
+lots
+of
+money
+I
+always
+felt
+like
+I
+could
+do
+it
+I
+came
+to
+a
+place
+one
+evenin
+where
+the
+road
+forked
+and
+I
+didnt
+know
+which
+fork
+to
+take
+I
+studied
+about
+it
+for
+half
+an
+hour
+and
+then
+I
+took
+the
+lefthand
+That
+night
+I
+run
+into
+the
+camp
+of
+a
+Wild
+West
+show
+that
+was
+travellin
+among
+the
+little
+towns
+and
+I
+went
+West
+with
+it
+Ive
+often
+wondered
+if
+I
+wouldnt
+have
+turned
+out
+different
+if
+Id
+took
+the
+other
+road
+Oh
+I
+reckon
+youd
+have
+ended
+up
+about
+the
+same
+said
+Bob
+Tidball
+cheerfully
+philosophical
+It
+aint
+the
+roads
+we
+take;its
+whats
+inside
+of
+us
+that
+makes
+us
+turn
+out
+the
+way
+we
+do
+Shark
+Dodson
+got
+up
+and
+leaned
+against
+a
+tree
+Id
+a
+good
+deal
+rather
+that
+sorrel
+of
+yourn
+hadnt
+hurt
+himself
+Bob
+he
+said
+again
+almost
+pathetically
+Same
+here
+agreed
+Bob;he
+was
+sure
+a
+firstrate
+kind
+of
+a
+crowbait
+But
+Bolivar
+hell
+pull
+us
+through
+all
+right
+Reckon
+wed
+better
+be
+movin
+on
+hadnt
+we
+Shark
+Ill
+bag
+this
+boodle
+agin
+and
+well
+hit
+the
+trail
+for
+higher
+timber
+Bob
+Tidball
+replaced
+the
+spoil
+in
+the
+bag
+and
+tied
+the
+mouth
+of
+it
+tightly
+with
+a
+cord
+When
+he
+looked
+up
+the
+most
+prominent
+object
+that
+he
+saw
+was
+the
+muzzle
+of
+Shark
+Dodsons
+45
+held
+upon
+him
+without
+a
+waver
+Stop
+your
+funnin
+said
+Bob
+with
+a
+grin
+We
+got
+to
+be
+hittin
+the
+breeze
+Set
+still
+said
+Shark
+You
+aint
+goin
+to
+hit
+no
+breeze
+Bob
+I
+hate
+to
+tell
+you
+but
+there
+aint
+any
+chance
+for
+but
+one
+of
+us
+Bolivar
+hes
+plenty
+tired
+and
+he
+cant
+carry
+double
+We
+been
+pards
+me
+and
+you
+Shark
+Dodson
+for
+three
+year
+Bob
+said
+quietly
+Weve
+risked
+our
+lives
+together
+time
+and
+again
+Ive
+always
+give
+you
+a
+square
+deal
+and
+I
+thought
+you
+was
+a
+man
+Ive
+heard
+some
+queer
+stories
+about
+you
+shootin
+one
+or
+two
+men
+in
+a
+peculiar
+way
+but
+I
+never
+believed
+em
+Now
+if
+youre
+just
+havin
+a
+little
+fun
+with
+me
+Shark
+put
+your
+gun
+up
+and
+well
+get
+on
+Bolivar
+and
+vamose
+If
+you
+mean
+to
+shoot
+shoot
+you
+blackhearted
+son
+of
+a
+tarantula
+Shark
+Dodsons
+face
+bore
+a
+deeply
+sorrowful
+look
+You
+dont
+know
+how
+bad
+I
+feel
+he
+sighed
+about
+that
+sorrel
+of
+yourn
+breakin
+his
+leg
+Bob
+The
+expression
+on
+Dodsons
+face
+changed
+in
+an
+instant
+to
+one
+of
+cold
+ferocity
+mingled
+with
+inexorable
+cupidity
+The
+soul
+of
+the
+man
+showed
+itself
+for
+a
+moment
+like
+an
+evil
+face
+in
+the
+window
+of
+a
+reputable
+house
+Truly
+Bob
+Tidball
+was
+never
+to
+hit
+the
+breeze
+again
+The
+deadly
+45
+of
+the
+false
+friend
+cracked
+and
+filled
+the
+gorge
+with
+a
+roar
+that
+the
+walls
+hurled
+back
+with
+indignant
+echoes
+And
+Bolivar
+unconscious
+accomplice
+swiftly
+bore
+away
+the
+last
+of
+the
+holdersup
+of
+the
+Sunset
+Express
+not
+put
+to
+the
+stress
+of
+carrying
+double
+But
+as
+Shark
+Dodson
+galloped
+away
+the
+woods
+seemed
+to
+fade
+from
+his
+view;the
+revolver
+in
+his
+right
+hand
+turned
+to
+the
+curved
+arm
+of
+a
+mahogany
+chair;his
+saddle
+was
+strangely
+upholstered
+and
+he
+opened
+his
+eyes
+and
+saw
+his
+feet
+not
+in
+stirrups
+but
+resting
+quietly
+on
+the
+edge
+of
+a
+quarteredoak
+desk
+I
+am
+telling
+you
+that
+Dodson
+of
+the
+firm
+of
+Dodson
+&Decker
+Wall
+Street
+brokers
+opened
+his
+eyes
+Peabody
+the
+confidential
+clerk
+was
+standing
+by
+his
+chair
+hesitating
+to
+speak
+There
+was
+a
+confused
+hum
+of
+wheels
+below
+and
+the
+sedative
+buzz
+of
+an
+electric
+fan
+Ahem
+Peabody
+said
+Dodson
+blinking
+I
+must
+have
+fallen
+asleep
+I
+had
+a
+most
+remarkable
+dream
+What
+is
+it
+Peabody
+Mr
+Williams
+sir
+of
+Tracy
+&Williams
+is
+outside
+He
+has
+come
+to
+settle
+his
+deal
+in
+X
+Y
+Z
+The
+market
+caught
+him
+short
+sir
+if
+you
+remember
+Yes
+I
+remember
+What
+is
+X
+Y
+Z
+quoted
+at
+today
+Peabody
+One
+eightyfive
+sir
+Then
+thats
+his
+price
+Excuse
+me
+said
+Peabody
+rather
+nervously
+for
+speaking
+of
+it
+but
+Ive
+been
+talking
+to
+Williams
+Hes
+an
+old
+friend
+of
+yours
+Mr
+Dodson
+and
+you
+practically
+have
+a
+corner
+in
+X
+Y
+Z
+I
+thought
+you
+might
+that
+is
+I
+thought
+you
+might
+not
+remember
+that
+he
+sold
+you
+the
+stock
+at
+98
+If
+he
+settles
+at
+the
+market
+price
+it
+will
+take
+every
+cent
+he
+has
+in
+the
+world
+and
+his
+home
+too
+to
+deliver
+the
+shares
+The
+expression
+on
+Dodsons
+face
+changed
+in
+an
+instant
+to
+one
+of
+cold
+ferocity
+mingled
+with
+inexorable
+cupidity
+The
+soul
+of
+the
+man
+showed
+itself
+for
+a
+moment
+like
+an
+evil
+face
+in
+the
+window
+of
+a
+reputable
+house
+He
+will
+settle
+at
+one
+eightyfive
+said
+Dodson
+Bolivar
+cannot
+carry
+double
\ No newline at end of file
diff --git a/TagsCloudApp/TagsCloudApp/packages.config b/TagsCloudApp/TagsCloudApp/packages.config
new file mode 100644
index 000000000..b7ecabef7
--- /dev/null
+++ b/TagsCloudApp/TagsCloudApp/packages.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/di.sln b/di.sln
index 9e820827b..638b84521 100644
--- a/di.sln
+++ b/di.sln
@@ -10,6 +10,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
README.md = README.md
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagsCloudApp", "TagsCloudApp\TagsCloudApp\TagsCloudApp.csproj", "{32340528-0E6D-4CA1-8048-545865F527B1}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -20,6 +22,10 @@ Global
{2A3ACF0E-1D07-4A1A-8320-F99DC2DE791F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2A3ACF0E-1D07-4A1A-8320-F99DC2DE791F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2A3ACF0E-1D07-4A1A-8320-F99DC2DE791F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {32340528-0E6D-4CA1-8048-545865F527B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {32340528-0E6D-4CA1-8048-545865F527B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {32340528-0E6D-4CA1-8048-545865F527B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {32340528-0E6D-4CA1-8048-545865F527B1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE