Skip to content

Commit 4c1d1ff

Browse files
authored
Major Rework - Placement, sizing, cleanliness, and speed. (#79)
* 🚧 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.
1 parent d40c799 commit 4c1d1ff

File tree

10 files changed

+2046
-1762
lines changed

10 files changed

+2046
-1762
lines changed

.vscode/settings.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"omnisharp.enableRoslynAnalyzers": true,
3+
"cSpell.words": [
4+
"AARRGGBB",
5+
"ARGB",
6+
"RRGGBB",
7+
"wcloud"
8+
]
9+
}

Module/PSWordCloudCmdlet.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
<PropertyGroup>
44
<TargetFramework>netcoreapp3.1</TargetFramework>
5+
<LangVersion>preview</LangVersion>
6+
<nullable>enable</nullable>
57
</PropertyGroup>
68

79
<ItemGroup>

Module/src/PSWordCloud/Attributes.cs

Lines changed: 102 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Collections;
33
using System.Collections.Generic;
4-
using System.Collections.ObjectModel;
54
using System.Linq;
65
using System.Management.Automation;
76
using System.Management.Automation.Language;
@@ -40,6 +39,8 @@ public class TransformToSKSizeIAttribute : ArgumentTransformationAttribute
4039
public override object Transform(EngineIntrinsics engineIntrinsics, object inputData)
4140
{
4241
int sideLength = 0;
42+
SKSizeI? result;
43+
4344
switch (inputData)
4445
{
4546
case SKSize sk:
@@ -72,75 +73,42 @@ public override object Transform(EngineIntrinsics engineIntrinsics, object input
7273
{
7374
sideLength = (int)ul;
7475
}
76+
7577
break;
7678
case decimal d:
7779
if (d <= int.MaxValue)
7880
{
7981
sideLength = (int)Math.Round(d);
8082
}
83+
8184
break;
8285
case float f:
8386
if (f <= int.MaxValue)
8487
{
8588
sideLength = (int)Math.Round(f);
8689
}
90+
8791
break;
8892
case double d:
8993
if (d <= int.MaxValue)
9094
{
9195
sideLength = (int)Math.Round(d);
9296
}
97+
9398
break;
9499
case string s:
95-
if (WCUtils.StandardImageSizes.ContainsKey(s))
96-
{
97-
return WCUtils.StandardImageSizes[s].Size;
98-
}
99-
else
100+
result = GetSizeFromString(s);
101+
if (result is not null)
100102
{
101-
var matchWH = Regex.Match(s, @"^(?<Width>[\d\.,]+)x(?<Height>[\d\.,]+)(px)?$");
102-
if (matchWH.Success)
103-
{
104-
try
105-
{
106-
var width = int.Parse(matchWH.Groups["Width"].Value);
107-
var height = int.Parse(matchWH.Groups["Height"].Value);
108-
109-
return new SKSizeI(width, height);
110-
}
111-
catch (Exception e)
112-
{
113-
throw new ArgumentTransformationMetadataException(
114-
"Could not parse input string as a float value", e);
115-
}
116-
}
117-
118-
var matchSide = Regex.Match(s, @"^(?<SideLength>[\d\.,]+)(px)?$");
119-
if (matchSide.Success)
120-
{
121-
sideLength = int.Parse(matchSide.Groups["SideLength"].Value);
122-
}
103+
return result;
123104
}
124105

125106
break;
126107
case object o:
127-
IEnumerable properties;
128-
if (o is Hashtable ht)
129-
{
130-
properties = ht;
131-
}
132-
else
108+
result = GetSizeFromProperties(o);
109+
if (result is not null)
133110
{
134-
properties = PSObject.AsPSObject(o).Properties;
135-
}
136-
137-
if (properties.GetValue("Width") != null && properties.GetValue("Height") != null)
138-
{
139-
// If these conversions fail, the exception will cause the transform to fail.
140-
var width = properties.GetValue("Width").ConvertTo<int>();
141-
var height = properties.GetValue("Height").ConvertTo<int>();
142-
143-
return new SKSizeI(width, height);
111+
return result;
144112
}
145113

146114
break;
@@ -154,6 +122,71 @@ public override object Transform(EngineIntrinsics engineIntrinsics, object input
154122
var errorMessage = $"Unrecognisable input '{inputData}' for SKSize parameter. See the help documentation for the parameter for allowed values.";
155123
throw new ArgumentTransformationMetadataException(errorMessage);
156124
}
125+
126+
private SKSizeI? GetSizeFromString(string str)
127+
{
128+
if (WCUtils.StandardImageSizes.ContainsKey(str))
129+
{
130+
return WCUtils.StandardImageSizes[str].Size;
131+
}
132+
else
133+
{
134+
var matchWH = Regex.Match(str, @"^(?<Width>[\d\.,]+)x(?<Height>[\d\.,]+)(px)?$");
135+
if (matchWH.Success)
136+
{
137+
try
138+
{
139+
var width = int.Parse(matchWH.Groups["Width"].Value);
140+
var height = int.Parse(matchWH.Groups["Height"].Value);
141+
142+
return new SKSizeI(width, height);
143+
}
144+
catch (Exception e)
145+
{
146+
throw new ArgumentTransformationMetadataException(
147+
"Could not parse input string as an integer value", e);
148+
}
149+
}
150+
151+
var matchSide = Regex.Match(str, @"^(?<SideLength>[\d\.,]+)(px)?$");
152+
if (matchSide.Success)
153+
{
154+
var sideLength = int.Parse(matchSide.Groups["SideLength"].Value);
155+
return new SKSizeI(sideLength, sideLength);
156+
}
157+
}
158+
159+
return null;
160+
}
161+
162+
private SKSizeI? GetSizeFromProperties(object obj)
163+
{
164+
IEnumerable properties;
165+
if (obj is Hashtable ht)
166+
{
167+
properties = ht;
168+
}
169+
else
170+
{
171+
properties = PSObject.AsPSObject(obj).Properties;
172+
}
173+
174+
if (properties.GetValue("Width") is not null && properties.GetValue("Height") is not null)
175+
{
176+
// If these conversions fail, the exception will cause the transform to fail.
177+
object? width = properties.GetValue("Width");
178+
object? height = properties.GetValue("Height");
179+
180+
if (width is null || height is null)
181+
{
182+
return null;
183+
}
184+
185+
return new SKSizeI(width.ConvertTo<int>(), height.ConvertTo<int>());
186+
}
187+
188+
return null;
189+
}
157190
}
158191

159192
public class FontFamilyCompleter : IArgumentCompleter
@@ -210,21 +243,24 @@ private static SKTypeface CreateTypefaceFromObject(object input)
210243
}
211244

212245
SKFontStyle style;
213-
if (properties.GetValue("FontWeight") != null
214-
|| properties.GetValue("FontSlant") != null
215-
|| properties.GetValue("FontWidth") != null)
246+
if (properties.GetValue("FontWeight") is not null
247+
|| properties.GetValue("FontSlant") is not null
248+
|| properties.GetValue("FontWidth") is not null)
216249
{
217-
SKFontStyleWeight weight = properties.GetValue("FontWeight") == null
250+
object? weightValue = properties.GetValue("FontWeight");
251+
SKFontStyleWeight weight = weightValue is null
218252
? SKFontStyleWeight.Normal
219-
: properties.GetValue("FontWeight").ConvertTo<SKFontStyleWeight>();
253+
: weightValue.ConvertTo<SKFontStyleWeight>();
220254

221-
SKFontStyleSlant slant = properties.GetValue("FontSlant") == null
255+
object? slantValue = properties.GetValue("FontSlant");
256+
SKFontStyleSlant slant = slantValue is null
222257
? SKFontStyleSlant.Upright
223-
: properties.GetValue("FontSlant").ConvertTo<SKFontStyleSlant>();
258+
: slantValue.ConvertTo<SKFontStyleSlant>();
224259

225-
SKFontStyleWidth width = properties.GetValue("FontWidth") == null
260+
object? widthValue = properties.GetValue("FontWidth");
261+
SKFontStyleWidth width = widthValue is null
226262
? SKFontStyleWidth.Normal
227-
: properties.GetValue("FontWidth").ConvertTo<SKFontStyleWidth>();
263+
: widthValue.ConvertTo<SKFontStyleWidth>();
228264

229265
style = new SKFontStyle(weight, width, slant);
230266
}
@@ -235,7 +271,7 @@ private static SKTypeface CreateTypefaceFromObject(object input)
235271
: SKFontStyle.Normal;
236272
}
237273

238-
string familyName = properties.GetValue("FamilyName").ConvertTo<string>();
274+
string familyName = properties.GetValue("FamilyName")?.ConvertTo<string>() ?? string.Empty;
239275
return WCUtils.FontManager.MatchFamily(familyName, style);
240276
}
241277

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

304-
byte red = properties.GetValue("red") == null
340+
object? redValue = properties.GetValue("red");
341+
byte red = redValue is null
305342
? (byte)0
306-
: properties.GetValue("red").ConvertTo<byte>();
343+
: redValue.ConvertTo<byte>();
307344

308-
byte green = properties.GetValue("green") == null
345+
object? greenValue = properties.GetValue("green");
346+
byte green = greenValue is null
309347
? (byte)0
310-
: properties.GetValue("green").ConvertTo<byte>();
348+
: greenValue.ConvertTo<byte>();
311349

312-
byte blue = properties.GetValue("blue") == null
350+
object? blueValue = properties.GetValue("blue");
351+
byte blue = blueValue is null
313352
? (byte)0
314-
: properties.GetValue("blue").ConvertTo<byte>();
353+
: blueValue.ConvertTo<byte>();
315354

316-
byte alpha = properties.GetValue("alpha") == null
355+
object? alphaValue = properties.GetValue("alpha");
356+
byte alpha = alphaValue is null
317357
? (byte)255
318-
: properties.GetValue("alpha").ConvertTo<byte>();
358+
: alphaValue.ConvertTo<byte>();
319359

320360
colorList.Add(new SKColor(red, green, blue, alpha));
321361
}

Module/src/PSWordCloud/Constants.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace PSWordCloud
2+
{
3+
internal static class Constants
4+
{
5+
internal const float BaseAngularIncrement = 360 / 7;
6+
internal const float BleedAreaScale = 1.5f;
7+
internal const float FocusWordScale = 1.15f;
8+
internal const float MaxWordAreaPercent = 0.9f;
9+
internal const float MaxWordWidthPercent = 0.95f;
10+
internal const int MinEffectiveWordWidth = 15;
11+
internal const float PaddingBaseScale = 1.5f;
12+
internal const float StrokeBaseScale = 0.01f;
13+
}
14+
}

0 commit comments

Comments
 (0)