Skip to content

Commit

Permalink
Major Rework - Placement, sizing, cleanliness, and speed. (#79)
Browse files Browse the repository at this point in the history
* 🚧 Rework placement, sizing, & cleanup

- Reworked placement algorithms and padding calculations.
- Reworked size estimation algorithms to be much more accurate, which
  makes scaling estimates much more appropriate for the image size and
  dimensions.
- Constrain maximum word widths based on a minimum word length so small
  words don't scale out of control and require several re-calcs to make
  things fit.

♻️ Split several things out into separate classes:

- Constants.cs - hold constants used for calculations.
- Extensions.cs - holds extension methods instead of WCUtils.
- LockingRandom.cs - class-based implementation of the threadsafe Random
  logic that was already being used.
- Word.cs - created a new type to simplify handling of words and sizes.

* ♻️ bump lang version & is not null

* 🔥 Remove `-Words` parameter alias

Too similar to `-WordSizes` which was a separate parameter.
  • Loading branch information
vexx32 authored Sep 14, 2020
1 parent d40c799 commit 4c1d1ff
Show file tree
Hide file tree
Showing 10 changed files with 2,046 additions and 1,762 deletions.
9 changes: 9 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"omnisharp.enableRoslynAnalyzers": true,
"cSpell.words": [
"AARRGGBB",
"ARGB",
"RRGGBB",
"wcloud"
]
}
2 changes: 2 additions & 0 deletions Module/PSWordCloudCmdlet.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<LangVersion>preview</LangVersion>
<nullable>enable</nullable>
</PropertyGroup>

<ItemGroup>
Expand Down
164 changes: 102 additions & 62 deletions Module/src/PSWordCloud/Attributes.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Management.Automation;
using System.Management.Automation.Language;
Expand Down Expand Up @@ -40,6 +39,8 @@ public class TransformToSKSizeIAttribute : ArgumentTransformationAttribute
public override object Transform(EngineIntrinsics engineIntrinsics, object inputData)
{
int sideLength = 0;
SKSizeI? result;

switch (inputData)
{
case SKSize sk:
Expand Down Expand Up @@ -72,75 +73,42 @@ public override object Transform(EngineIntrinsics engineIntrinsics, object input
{
sideLength = (int)ul;
}

break;
case decimal d:
if (d <= int.MaxValue)
{
sideLength = (int)Math.Round(d);
}

break;
case float f:
if (f <= int.MaxValue)
{
sideLength = (int)Math.Round(f);
}

break;
case double d:
if (d <= int.MaxValue)
{
sideLength = (int)Math.Round(d);
}

break;
case string s:
if (WCUtils.StandardImageSizes.ContainsKey(s))
{
return WCUtils.StandardImageSizes[s].Size;
}
else
result = GetSizeFromString(s);
if (result is not null)
{
var matchWH = Regex.Match(s, @"^(?<Width>[\d\.,]+)x(?<Height>[\d\.,]+)(px)?$");
if (matchWH.Success)
{
try
{
var width = int.Parse(matchWH.Groups["Width"].Value);
var height = int.Parse(matchWH.Groups["Height"].Value);

return new SKSizeI(width, height);
}
catch (Exception e)
{
throw new ArgumentTransformationMetadataException(
"Could not parse input string as a float value", e);
}
}

var matchSide = Regex.Match(s, @"^(?<SideLength>[\d\.,]+)(px)?$");
if (matchSide.Success)
{
sideLength = int.Parse(matchSide.Groups["SideLength"].Value);
}
return result;
}

break;
case object o:
IEnumerable properties;
if (o is Hashtable ht)
{
properties = ht;
}
else
result = GetSizeFromProperties(o);
if (result is not null)
{
properties = PSObject.AsPSObject(o).Properties;
}

if (properties.GetValue("Width") != null && properties.GetValue("Height") != null)
{
// If these conversions fail, the exception will cause the transform to fail.
var width = properties.GetValue("Width").ConvertTo<int>();
var height = properties.GetValue("Height").ConvertTo<int>();

return new SKSizeI(width, height);
return result;
}

break;
Expand All @@ -154,6 +122,71 @@ public override object Transform(EngineIntrinsics engineIntrinsics, object input
var errorMessage = $"Unrecognisable input '{inputData}' for SKSize parameter. See the help documentation for the parameter for allowed values.";
throw new ArgumentTransformationMetadataException(errorMessage);
}

private SKSizeI? GetSizeFromString(string str)
{
if (WCUtils.StandardImageSizes.ContainsKey(str))
{
return WCUtils.StandardImageSizes[str].Size;
}
else
{
var matchWH = Regex.Match(str, @"^(?<Width>[\d\.,]+)x(?<Height>[\d\.,]+)(px)?$");
if (matchWH.Success)
{
try
{
var width = int.Parse(matchWH.Groups["Width"].Value);
var height = int.Parse(matchWH.Groups["Height"].Value);

return new SKSizeI(width, height);
}
catch (Exception e)
{
throw new ArgumentTransformationMetadataException(
"Could not parse input string as an integer value", e);
}
}

var matchSide = Regex.Match(str, @"^(?<SideLength>[\d\.,]+)(px)?$");
if (matchSide.Success)
{
var sideLength = int.Parse(matchSide.Groups["SideLength"].Value);
return new SKSizeI(sideLength, sideLength);
}
}

return null;
}

private SKSizeI? GetSizeFromProperties(object obj)
{
IEnumerable properties;
if (obj is Hashtable ht)
{
properties = ht;
}
else
{
properties = PSObject.AsPSObject(obj).Properties;
}

if (properties.GetValue("Width") is not null && properties.GetValue("Height") is not null)
{
// If these conversions fail, the exception will cause the transform to fail.
object? width = properties.GetValue("Width");
object? height = properties.GetValue("Height");

if (width is null || height is null)
{
return null;
}

return new SKSizeI(width.ConvertTo<int>(), height.ConvertTo<int>());
}

return null;
}
}

public class FontFamilyCompleter : IArgumentCompleter
Expand Down Expand Up @@ -210,21 +243,24 @@ private static SKTypeface CreateTypefaceFromObject(object input)
}

SKFontStyle style;
if (properties.GetValue("FontWeight") != null
|| properties.GetValue("FontSlant") != null
|| properties.GetValue("FontWidth") != null)
if (properties.GetValue("FontWeight") is not null
|| properties.GetValue("FontSlant") is not null
|| properties.GetValue("FontWidth") is not null)
{
SKFontStyleWeight weight = properties.GetValue("FontWeight") == null
object? weightValue = properties.GetValue("FontWeight");
SKFontStyleWeight weight = weightValue is null
? SKFontStyleWeight.Normal
: properties.GetValue("FontWeight").ConvertTo<SKFontStyleWeight>();
: weightValue.ConvertTo<SKFontStyleWeight>();

SKFontStyleSlant slant = properties.GetValue("FontSlant") == null
object? slantValue = properties.GetValue("FontSlant");
SKFontStyleSlant slant = slantValue is null
? SKFontStyleSlant.Upright
: properties.GetValue("FontSlant").ConvertTo<SKFontStyleSlant>();
: slantValue.ConvertTo<SKFontStyleSlant>();

SKFontStyleWidth width = properties.GetValue("FontWidth") == null
object? widthValue = properties.GetValue("FontWidth");
SKFontStyleWidth width = widthValue is null
? SKFontStyleWidth.Normal
: properties.GetValue("FontWidth").ConvertTo<SKFontStyleWidth>();
: widthValue.ConvertTo<SKFontStyleWidth>();

style = new SKFontStyle(weight, width, slant);
}
Expand All @@ -235,7 +271,7 @@ private static SKTypeface CreateTypefaceFromObject(object input)
: SKFontStyle.Normal;
}

string familyName = properties.GetValue("FamilyName").ConvertTo<string>();
string familyName = properties.GetValue("FamilyName")?.ConvertTo<string>() ?? string.Empty;
return WCUtils.FontManager.MatchFamily(familyName, style);
}

Expand Down Expand Up @@ -301,21 +337,25 @@ private SKColor[] TransformObject(object input)
properties = PSObject.AsPSObject(item).Properties;
}

byte red = properties.GetValue("red") == null
object? redValue = properties.GetValue("red");
byte red = redValue is null
? (byte)0
: properties.GetValue("red").ConvertTo<byte>();
: redValue.ConvertTo<byte>();

byte green = properties.GetValue("green") == null
object? greenValue = properties.GetValue("green");
byte green = greenValue is null
? (byte)0
: properties.GetValue("green").ConvertTo<byte>();
: greenValue.ConvertTo<byte>();

byte blue = properties.GetValue("blue") == null
object? blueValue = properties.GetValue("blue");
byte blue = blueValue is null
? (byte)0
: properties.GetValue("blue").ConvertTo<byte>();
: blueValue.ConvertTo<byte>();

byte alpha = properties.GetValue("alpha") == null
object? alphaValue = properties.GetValue("alpha");
byte alpha = alphaValue is null
? (byte)255
: properties.GetValue("alpha").ConvertTo<byte>();
: alphaValue.ConvertTo<byte>();

colorList.Add(new SKColor(red, green, blue, alpha));
}
Expand Down
14 changes: 14 additions & 0 deletions Module/src/PSWordCloud/Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace PSWordCloud
{
internal static class Constants
{
internal const float BaseAngularIncrement = 360 / 7;
internal const float BleedAreaScale = 1.5f;
internal const float FocusWordScale = 1.15f;
internal const float MaxWordAreaPercent = 0.9f;
internal const float MaxWordWidthPercent = 0.95f;
internal const int MinEffectiveWordWidth = 15;
internal const float PaddingBaseScale = 1.5f;
internal const float StrokeBaseScale = 0.01f;
}
}
Loading

0 comments on commit 4c1d1ff

Please sign in to comment.