Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Холстинин Егор #167

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions TagsCloudVisualization/BitmapExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Drawing;
using System.Drawing.Imaging;

namespace TagsCloudVisualization;

public static class BitmapExtensions
{
public static void SaveImage(this Bitmap bitmap, string outputFilePath, ImageFormat imageFormat)
{
outputFilePath = Path.GetFullPath(outputFilePath);
var outputFileName = Path.GetFileName(outputFilePath);
var outputFileDirectory = Path.GetDirectoryName(outputFilePath);

Directory.CreateDirectory(outputFileDirectory);

var savePath = Path.Combine(outputFileDirectory, $"{outputFileName}.{imageFormat.ToString().ToLower()}");

bitmap.Save(savePath, imageFormat);

Console.WriteLine($"Image is saved to {savePath}");
}
}
27 changes: 27 additions & 0 deletions TagsCloudVisualization/CustomAttributes/MustBeEnumNameAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.ComponentModel.DataAnnotations;

namespace TagsCloudVisualization.CustomAttributes;

public class MustBeEnumNameAttribute : ValidationAttribute
{
private Type enumType;

public MustBeEnumNameAttribute(Type enumType)
: base()
{
this.enumType = enumType;
}

protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
var enumNames = Enum.GetNames(enumType);
if (value is string valueString && Enum.TryParse(enumType, valueString, true, out var result)
&& Enum.IsDefined(enumType, result))
{
return ValidationResult.Success;
}

return new ValidationResult($"The value for {validationContext.DisplayName} must be in {enumType}. " +
$"Available: {String.Join(", ", enumNames)}");
}
}
21 changes: 21 additions & 0 deletions TagsCloudVisualization/CustomAttributes/MustBePositiveAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;

namespace TagsCloudVisualization.CustomAttributes;

public class MustBePositiveAttribute : ValidationAttribute
{
public MustBePositiveAttribute()
: base("The value for {0} must be bigger than 0")
{
}

protected override ValidationResult? IsValid(object? value, ValidationContext validationContext)
{
if (int.TryParse(value as string, out var number) && number > 0)
{
return ValidationResult.Success;
}

return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
}
6 changes: 6 additions & 0 deletions TagsCloudVisualization/DullCheckers/IDullWordChecker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace TagsCloudVisualization;

public interface IDullWordChecker
{
public bool Check(WordAnalysis word);
}
34 changes: 34 additions & 0 deletions TagsCloudVisualization/DullCheckers/MystemDullWordChecker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.Text;

namespace TagsCloudVisualization;

public class MystemDullWordChecker : IDullWordChecker
{
private HashSet<string> removedPartOfSpeech;
private HashSet<string> excludedWords = new();

public MystemDullWordChecker(TagLayoutSettings tagLayoutSettings)
{
removedPartOfSpeech = tagLayoutSettings.RemovedPartOfSpeech;
var excludedWordsFile = tagLayoutSettings.ExcludedWordsFile;
if (excludedWordsFile is null)
return;

try
{
excludedWords =
new HashSet<string>(File.ReadAllText(excludedWordsFile, Encoding.UTF8).Split(Environment.NewLine));
}
catch (FileNotFoundException e)
{
Console.WriteLine($"Could not find specified excluded words file {excludedWordsFile}. " +
$"No words will be excluded.");
}
}

public bool Check(WordAnalysis wordAnalysis)
{
return removedPartOfSpeech.Any(dullPart => wordAnalysis.GrammarAnalysis.StartsWith(dullPart))
|| excludedWords.Contains(wordAnalysis.Lexema.ToLower());
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions TagsCloudVisualization/IRectangleLayouter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Drawing;

namespace TagsCloudVisualization;

public interface IRectangleLayouter
{
public Rectangle PutNextRectangle(Size rectangleSize);
public Rectangle PutNextRectangle(SizeF rectangleSize);
}
86 changes: 86 additions & 0 deletions TagsCloudVisualization/LayoutDrawer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System.Drawing.Imaging;
using System.Drawing;

namespace TagsCloudVisualization;

public class LayoutDrawer
{
private IInterestingWordsParser interestingWordsParser;
private IRectangleLayouter rectangleLayouter;
private IPalette palette;
private Font font;

public LayoutDrawer(IInterestingWordsParser interestingWordsParser,
IRectangleLayouter rectangleLayouter,
IPalette palette,
Font font)
{
this.interestingWordsParser = interestingWordsParser;
this.rectangleLayouter = rectangleLayouter;
this.palette = palette;
this.font = font;
if (string.Compare(font.OriginalFontName, font.Name, StringComparison.InvariantCultureIgnoreCase) != 0)
Console.WriteLine($"Font \"{font.OriginalFontName}\" was not found. Using \"{font.Name}\" instead");
}

public Result<Bitmap> CreateLayoutImageFromFile(string inputFilePath,
Size imageSize,
int minimumFontSize)
{
return interestingWordsParser
.GetInterestingWords(inputFilePath)
.Then(GetSortedInterestingWordsCount)
.Then(rectangles => DrawRectangles(imageSize, minimumFontSize, rectangles))
.RefineError("Can't create layout image");
}

private Bitmap DrawRectangles(Size imageSize,
int minimumFontSize,
IOrderedEnumerable<(string Word, int Count)> sortedWordsCount)
{
Bitmap bitmap;
try
{
bitmap = new Bitmap(imageSize.Width, imageSize.Height);
}
catch (ArgumentException e)
{
throw new ArgumentException("Can't create image with negative width or height");
}

using var graphics = Graphics.FromImage(bitmap);
var mostWordOccurrencies = sortedWordsCount.Max(arg => arg.Count);

graphics.Clear(palette.GetBackgroundColor());

foreach (var wordCount in sortedWordsCount)
{
var rectangleFont = new Font(font.FontFamily,
Math.Max(font.Size * wordCount.Count / mostWordOccurrencies, minimumFontSize));
var rectangleSize = graphics.MeasureString(wordCount.Word, rectangleFont);

var textRectangle = rectangleLayouter.PutNextRectangle(rectangleSize);
var x = textRectangle.X + imageSize.Width / 2;
var y = textRectangle.Y + imageSize.Height / 2;

if (x < 0 || x > imageSize.Width || y < 0 || y > imageSize.Height)
throw new ApplicationException("Words went out of image boundaries");

using var brush = new SolidBrush(palette.GetNextWordColor());
graphics.DrawString(wordCount.Word, rectangleFont, brush, x, y);
}

return bitmap;
}

private IOrderedEnumerable<(string Word, int Count)> GetSortedInterestingWordsCount(
IEnumerable<string> interestingWords)
{
var sortedWordsCount = interestingWords
.GroupBy(s => s)
.Select(group => (Word: group.Key, Count: group.Count()))
.OrderByDescending(wordCount => wordCount.Count);

return sortedWordsCount;
}
}
10 changes: 10 additions & 0 deletions TagsCloudVisualization/Palletes/IPalette.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Drawing;

namespace TagsCloudVisualization;

public interface IPalette
{
public Color GetNextWordColor();

public Color GetBackgroundColor();
}
27 changes: 27 additions & 0 deletions TagsCloudVisualization/Palletes/Palette.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Drawing;

namespace TagsCloudVisualization;

public class Palette : IPalette
{
public Palette(Color[] textColor, Color backgroundColor)
{
TextColor = textColor;
BackgroundColor = backgroundColor;
}

private Color[] TextColor { get; set; }
private int currentColorId = 0;
private Color BackgroundColor { get; set; }

public Color GetNextWordColor()
{
if (currentColorId >= TextColor.Length) currentColorId = 0;
return TextColor[currentColorId++];
}

public Color GetBackgroundColor()
{
return BackgroundColor;
}
}
7 changes: 7 additions & 0 deletions TagsCloudVisualization/PointGenerators/Algorithm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace TagsCloudVisualization;

public enum Algorithm
{
Spiral,
Square
}
9 changes: 9 additions & 0 deletions TagsCloudVisualization/PointGenerators/IPointGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Drawing;

namespace TagsCloudVisualization;

public interface IPointGenerator
{
public Algorithm Name { get; }
Point GetNextPoint();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Drawing;

namespace TagsCloudVisualization;

public class LissajousCurvePointGenerator : IPointGenerator
{
private int xAmplitude = 100;
private int yAmplitude = 100;
private int xConstant = 19;
private int yConstant = 20;
private double delta = Math.PI / 2;
private double parameter = 0;

public Algorithm Name { get; } = Algorithm.Square;

public Point GetNextPoint()
{
parameter += 0.01;
var x = Math.Round(xAmplitude * Math.Sin(xConstant * parameter + delta));
var y = Math.Round(yAmplitude * Math.Sin(yConstant * parameter));

if (parameter > 20)
{
parameter = 0;
xAmplitude += 20;
yAmplitude += 20;
}

return new Point((int)x, (int)y);
}
}
28 changes: 28 additions & 0 deletions TagsCloudVisualization/PointGenerators/SpiralPointGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Drawing;

namespace TagsCloudVisualization;

public class SpiralPointGenerator : IPointGenerator
{
public Point Center { get; } = new(0, 0);
public int Radius { get; private set; }
public double Angle { get; private set; }
public int RadiusDelta { get; private set; } = 1;
public double AngleDelta { get; private set; } = Math.PI / 60;

public Algorithm Name { get; } = Algorithm.Spiral;

public Point GetNextPoint()
{
var x = (int)Math.Round(Center.X + Radius * Math.Cos(Angle));
var y = (int)Math.Round(Center.Y + Radius * Math.Sin(Angle));

var nextAngle = Angle + AngleDelta;
var angleMoreThan2Pi = Math.Abs(nextAngle) >= Math.PI * 2;

Radius = angleMoreThan2Pi ? Radius + RadiusDelta : Radius;
Angle = angleMoreThan2Pi ? 0 : nextAngle;

return new Point(x, y);
}
}
Loading