1 /* 2 * "Word Cloud (Tag Cloud)". 3 * Copyright (C) 2011 by George Mamaladze. 4 * http://sourcecodecloud.codeplex.com/ 5 * https://www.codeproject.com/Articles/224231/Word-Cloud-Tag-Cloud-Generator-Control-for-NET-Win 6 * 7 * This licensed under The Code Project Open License (CPOL). 8 * 9 * Adapted for the GEDKeeper project by Sergey V. Zhdanovskih in September 2017. 10 */ 11 12 using System; 13 using System.Collections.Generic; 14 using System.Drawing; 15 16 namespace GKWordsCloudPlugin.WordsCloud 17 { 18 public interface ICloudRenderer : IDisposable 19 { Measure(string text, int weight)20 SizeF Measure(string text, int weight); Draw(Word word, bool highlight)21 void Draw(Word word, bool highlight); 22 } 23 24 public class CloudModel 25 { 26 private readonly PointF fCenter; 27 private readonly QuadTree<Word> fQuadTree; 28 private readonly RectangleF fSurface; 29 CloudModel(SizeF size)30 public CloudModel(SizeF size) 31 { 32 fSurface = new RectangleF(new PointF(0, 0), size); 33 fQuadTree = new QuadTree<Word>(fSurface); 34 fCenter = new PointF(fSurface.X + size.Width / 2, fSurface.Y + size.Height / 2); 35 } 36 Arrange(List<Word> words, ICloudRenderer renderer)37 public void Arrange(List<Word> words, ICloudRenderer renderer) 38 { 39 if (words == null) { 40 throw new ArgumentNullException("words"); 41 } 42 43 foreach (Word word in words) { 44 SizeF size = renderer.Measure(word.Text, word.Occurrences); 45 46 RectangleF freeRectangle; 47 if (TryFindFreeRectangle(size, out freeRectangle)) { 48 word.Rectangle = freeRectangle; 49 word.IsExposed = true; 50 fQuadTree.Insert(word); 51 } 52 } 53 } 54 GetWordsInArea(RectangleF area)55 public IEnumerable<Word> GetWordsInArea(RectangleF area) 56 { 57 return fQuadTree.Query(area); 58 } 59 IsInsideSurface(RectangleF target)60 private bool IsInsideSurface(RectangleF target) 61 { 62 return target.X >= fSurface.X && target.Y >= fSurface.Y && 63 target.Bottom <= fSurface.Bottom && target.Right <= fSurface.Right; 64 } 65 TryFindFreeRectangle(SizeF size, out RectangleF foundRectangle)66 public bool TryFindFreeRectangle(SizeF size, out RectangleF foundRectangle) 67 { 68 foundRectangle = RectangleF.Empty; 69 double alpha = GetStartPseudoAngle(size); 70 const double stepAlpha = Math.PI / 60; 71 72 const double pointsOnSpital = 500; 73 74 for (int pointIndex = 0; pointIndex < pointsOnSpital; pointIndex++) { 75 double dX = pointIndex / pointsOnSpital * Math.Sin(alpha) * fCenter.X; 76 double dY = pointIndex / pointsOnSpital * Math.Cos(alpha) * fCenter.Y; 77 foundRectangle = new RectangleF((float)(fCenter.X + dX) - size.Width / 2, (float)(fCenter.Y + dY) - size.Height / 2, size.Width, size.Height); 78 79 alpha += stepAlpha; 80 if (!IsInsideSurface(foundRectangle)) { 81 return false; 82 } 83 84 if (!fQuadTree.HasContent(foundRectangle)) { 85 return true; 86 } 87 } 88 89 return false; 90 } 91 GetStartPseudoAngle(SizeF size)92 private static float GetStartPseudoAngle(SizeF size) 93 { 94 return size.Height * size.Width; 95 } 96 } 97 } 98