Skip to content

Commit

Permalink
Avoid state sharing (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
jjonescz authored Dec 10, 2022
2 parents 4bc5acc + a901809 commit f0b9550
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 3 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ This library is also used in production by [KnowledgePicker](https://knowledgepi

As mentioned [above](#how-to-use), only subset of functionality is implemented now, but all contributions are welcome. Feel free to open [issues](https://github.com/knowledgepicker/word-cloud/issues) and [pull requests](https://github.com/knowledgepicker/word-cloud/pulls).

### Testing

Tests are currently only supported on Linux, because they are snapshot tests (generating a word cloud image and comparing it byte-by-byte with a snapshot) and more work is needed to ensure this is cross-platform (e.g., use exactly the same font). On Windows, tests can be run in WSL (Visual Studio supports this directly). Tests are also automatically run in GitHub Actions.

### Release process

After pushing a tag, GitHub workflow `release.yml` is triggered which builds and publishes the NuGet package.
2 changes: 2 additions & 0 deletions src/KnowledgePicker.WordCloud/Drawing/IGraphicEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,7 @@ public interface IGraphicEngine : IDisposable
public interface IGraphicEngine<TBitmap> : IGraphicEngine
{
TBitmap Bitmap { get; }

IGraphicEngine<TBitmap> Clone();
}
}
15 changes: 15 additions & 0 deletions src/KnowledgePicker.WordCloud/Drawing/SkGraphicEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ public sealed class SkGraphicEngine : IGraphicEngine<SKBitmap>
private readonly SKPaint textPaint;
private readonly WordCloudInput wordCloud;

private SkGraphicEngine(ISizer sizer, WordCloudInput wordCloud,
SKPaint textPaint)
{
Sizer = sizer;
this.wordCloud = wordCloud;
this.textPaint = textPaint;
Bitmap = new SKBitmap(wordCloud.Width, wordCloud.Height);
canvas = new SKCanvas(Bitmap);
}

public SkGraphicEngine(ISizer sizer, WordCloudInput wordCloud,
SKTypeface? font = null, bool antialias = true)
{
Expand Down Expand Up @@ -52,6 +62,11 @@ public void Draw(PointD location, RectangleD measured, string text, int count, s
(float)(location.Y - measured.Top), textPaint);
}

public IGraphicEngine<SKBitmap> Clone()
{
return new SkGraphicEngine(Sizer, wordCloud, textPaint);
}

public void Dispose()
{
textPaint.Dispose();
Expand Down
2 changes: 2 additions & 0 deletions src/KnowledgePicker.WordCloud/Layouts/BaseLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ public IEnumerable<LayoutItem> GetWordsInArea(RectangleD area)
return QuadTree.Query(area);
}

public abstract ILayout Clone();

protected bool IsInsideSurface(RectangleD targetRectangle)
{
return IsInside(Surface, targetRectangle);
Expand Down
1 change: 1 addition & 0 deletions src/KnowledgePicker.WordCloud/Layouts/ILayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ public interface ILayout
{
int Arrange(IEnumerable<WordCloudEntry> entries, IGraphicEngine engine);
IEnumerable<LayoutItem> GetWordsInArea(RectangleD area);
ILayout Clone();
}
}
5 changes: 5 additions & 0 deletions src/KnowledgePicker.WordCloud/Layouts/SpiralLayout.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ public override bool TryFindFreeRectangle(SizeD size, out RectangleD foundRectan
return false;
}

public override ILayout Clone()
{
return new SpiralLayout(WordCloud);
}

private static double GetPseudoRandomStartAngle(SizeD size)
{
return size.Height * size.Width;
Expand Down
2 changes: 1 addition & 1 deletion src/KnowledgePicker.WordCloud/Primitives/LayoutItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ namespace KnowledgePicker.WordCloud.Primitives
/// <summary>
/// Word arranged somewhere in word cloud.
/// </summary>
public class LayoutItem
public record LayoutItem
{
public LayoutItem(WordCloudEntry entry, PointD location, RectangleD measured)
{
Expand Down
10 changes: 8 additions & 2 deletions src/KnowledgePicker.WordCloud/WordCloudGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,19 @@ public WordCloudGenerator(WordCloudInput wordCloud,
private T Process<T>(
Func<IGraphicEngine<TBitmap>, IEnumerable<LayoutItem>, T> handler)
{
// Ensure state is not shared.
// TODO: We should instead use factory pattern.
// But that would be a big change in usage of this class.
var localEngine = engine.Clone();
var localLayout = layout.Clone();

// Arrange word cloud.
var size = new SizeD(wordCloud.Width, wordCloud.Height);
layout.Arrange(wordCloud.Entries, engine);
localLayout.Arrange(wordCloud.Entries, localEngine);

// Process results.
var area = new RectangleD(new PointD(0, 0), size);
return handler(engine, layout.GetWordsInArea(area));
return handler(localEngine, localLayout.GetWordsInArea(area));
}

public IEnumerable<(LayoutItem Item, double FontSize)> Arrange()
Expand Down
39 changes: 39 additions & 0 deletions test/KnowledgePicker.WordCloud.Tests/WordCloudGeneratorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using KnowledgePicker.WordCloud.Drawing;
using KnowledgePicker.WordCloud.Layouts;
using KnowledgePicker.WordCloud.Primitives;
using KnowledgePicker.WordCloud.Sizers;
using SkiaSharp;

namespace KnowledgePicker.WordCloud.Tests;

public class WordCloudGeneratorTests
{
[Fact] // https://github.com/knowledgepicker/word-cloud/issues/17
public void DoesNotShareState()
{
// Arrange.
var wordCloud = new WordCloudInput(new[]
{
new WordCloudEntry("a", 1),
new WordCloudEntry("b", 1),
})
{
Width = 1024,
Height = 256,
MinFontSize = 8,
MaxFontSize = 32
};
var sizer = new LogSizer(wordCloud);
using var engine = new SkGraphicEngine(sizer, wordCloud);
var layout = new SpiralLayout(wordCloud);
var wcg = new WordCloudGenerator<SKBitmap>(wordCloud, engine, layout);

// Act.
var result1 = wcg.Arrange().ToArray();
var result2 = wcg.Arrange().ToArray();

// Assert.
Assert.Equal(result1.AsEnumerable(), result2);
Assert.Equal(2, result2.Length);
}
}

0 comments on commit f0b9550

Please sign in to comment.