Skip to content

Commit 1a3b95f

Browse files
committed
Implemented grid selection menu with grid icons
Added 'Golden Circles' grid
1 parent 4f213cc commit 1a3b95f

File tree

13 files changed

+486
-16
lines changed

13 files changed

+486
-16
lines changed
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
using NUnit.Framework;
2+
using ScreenGrid.Models.Grids;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Drawing;
6+
using System.Globalization;
7+
using System.IO;
8+
using System.Text;
9+
10+
namespace ScreenGrid.Tests
11+
{
12+
/// <summary>
13+
/// Generating bitmap (PNG) icons for grid selection menu
14+
/// </summary>
15+
[TestFixture]
16+
class GridImagesHelper
17+
{
18+
private const int ImageWidth = 64;
19+
private const int ImageHeight = 48;
20+
21+
[Test]
22+
public void RenderGridImagesAsPngFiles()
23+
{
24+
foreach (GridType gridType in Enum.GetValues(typeof(GridType)))
25+
{
26+
RenderGridImageAsPngFile(gridType);
27+
}
28+
}
29+
30+
private static void RenderGridImageAsPngFile(GridType gridType)
31+
{
32+
var gridLines = GridCreator.CreateGrid(gridType, ImageWidth, ImageHeight, false);
33+
34+
var outputPath = Path.Combine(Path.GetTempPath(), "ScreenGrid");
35+
if (!Directory.Exists(outputPath))
36+
{
37+
Directory.CreateDirectory(outputPath);
38+
}
39+
40+
using (var image = new Bitmap(ImageWidth, ImageHeight))
41+
{
42+
using (var g = Graphics.FromImage(image))
43+
{
44+
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
45+
g.Clear(Color.Transparent);
46+
using (var pen = new Pen(Color.Black))
47+
{
48+
foreach (var line in gridLines)
49+
{
50+
g.DrawLine(
51+
pen,
52+
new Point((int)(line.p1.X * (ImageWidth - 1)), (int)(line.p1.Y * (ImageHeight - 1))),
53+
new Point((int)(line.p2.X * (ImageWidth - 1)), (int)(line.p2.Y * (ImageHeight - 1))));
54+
}
55+
}
56+
}
57+
58+
image.Save(Path.Combine(outputPath, gridType.ToString() + ".png"), System.Drawing.Imaging.ImageFormat.Png);
59+
}
60+
}
61+
62+
[Test]
63+
public void RenderGridImagesAsResourceDictionaryXamlPaths()
64+
{
65+
var lines = new List<string>();
66+
foreach (GridType gridType in Enum.GetValues(typeof(GridType)))
67+
{
68+
if (gridType != GridType.None)
69+
{
70+
var key = "pathDataGridType" + gridType.ToString();
71+
var path = RenderGridImageAsXamlPath(gridType);
72+
73+
lines.Add(String.Empty);
74+
lines.Add("<sys:String x:Key=\"" + key + "\">");
75+
lines.Add(path);
76+
lines.Add("</sys:String>");
77+
}
78+
}
79+
80+
File.WriteAllLines(Path.Combine(Path.GetTempPath(), "Grids.xaml"), lines);
81+
}
82+
83+
private static string RenderGridImageAsXamlPath(GridType gridType)
84+
{
85+
var gridLines = GridCreator.CreateGrid(gridType, ImageWidth, ImageHeight, false);
86+
87+
var result = new StringBuilder();
88+
foreach (var line in gridLines)
89+
{
90+
var command = String.Format(CultureInfo.InvariantCulture, "M{0:F4},{1:F4}L{2:F4},{3:F4}", line.p1.X, line.p1.Y, line.p2.X, line.p2.Y);
91+
result.Append(command);
92+
}
93+
94+
return result.ToString();
95+
}
96+
}
97+
}

Src/ScreenGrid.Tests/ScreenGrid.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
</ItemGroup>
4949
<ItemGroup>
5050
<Compile Include="FlatImageTests.cs" />
51+
<Compile Include="GridImagesHelper.cs" />
5152
<Compile Include="IntegerSegmentTests.cs" />
5253
<Compile Include="Properties\AssemblyInfo.cs" />
5354
<Compile Include="Properties\Resources.Designer.cs">

Src/ScreenGrid/App.xaml.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,20 @@ public App()
1414
protected override void OnStartup(StartupEventArgs e)
1515
{
1616
base.OnStartup(e);
17+
1718
this.DispatcherUnhandledException += AppDispatcherUnhandledException;
1819

19-
(new Views.ScreenGridWindow
20+
var vm = new ViewModels.ScreenGridViewModel();
21+
var view = new Views.ScreenGridWindow
2022
{
21-
DataContext = new ViewModels.ScreenGridViewModel()
22-
}).Show();
23+
DataContext = vm,
24+
};
25+
26+
view.Show();
27+
28+
// Use for easy debug
29+
////vm.GridMode = Models.Grids.GridType.GoldenCircles;
30+
////vm.SelectedLineColor = new ViewModels.ColorItemViewModel(System.Windows.Media.Colors.Black);
2331
}
2432

2533
private void CurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)

Src/ScreenGrid/Models/Grids/GridCreator.cs

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public static Point TransformPoint(
8282
/// <param name="height">Height of output image</param>
8383
/// <param name="isRotated">Output grid is rotated (width swapped with height)</param>
8484
/// <returns>List of lines</returns>
85-
public static IEnumerable<Line> CreateGrid(GridType gridType, double width, double height, bool isRotated)
85+
public static IList<Line> CreateGrid(GridType gridType, double width, double height, bool isRotated)
8686
{
8787
var actualAspectRatio = CalculateAspectRatio(width, height, isRotated);
8888

@@ -294,7 +294,7 @@ public static IEnumerable<Line> CreateGrid(GridType gridType, double width, doub
294294
const double FullTurn = 2.0 * Math.PI;
295295
const double MaxAngle = ((double)NumberOfTurns) * FullTurn;
296296

297-
for (var theta = 0.0; theta < MaxAngle; )
297+
for (var theta = 0.0; theta < MaxAngle;)
298298
{
299299
var r = Math.Pow(RatioConstants.GoldenSpiralCInRadians, theta);
300300

@@ -413,6 +413,25 @@ public static IEnumerable<Line> CreateGrid(GridType gridType, double width, doub
413413
lines.Add(new Line(rectangle.Right, rectangle.Top, rectangle.Right - w, rectangle.Bottom));
414414
lines.Add(new Line(rectangle.Right, rectangle.Bottom, rectangle.Right - w, rectangle.Top));
415415

416+
return lines;
417+
}
418+
case GridType.GoldenCircles:
419+
{
420+
var lines = new List<Line>();
421+
422+
var rectangle = CalculateDynamicRectangleExtents(RatioConstants.Phi, actualAspectRatio);
423+
var centerLine = rectangle.Top + rectangle.Height / 2.0;
424+
425+
// Major circles
426+
var majorRadius = (rectangle.Width * RatioConstants.Phi5D8) / 2.0;
427+
lines.AddRange(CreateCirclePoints(rectangle.Left + majorRadius, centerLine, majorRadius, actualAspectRatio));
428+
lines.AddRange(CreateCirclePoints(rectangle.Right - majorRadius, centerLine, majorRadius, actualAspectRatio));
429+
430+
// Minor circles
431+
var minorRadius = (rectangle.Width * RatioConstants.Phi3D8) / 2.0;
432+
lines.AddRange(CreateCirclePoints(rectangle.Left + minorRadius, centerLine, minorRadius, actualAspectRatio));
433+
lines.AddRange(CreateCirclePoints(rectangle.Right - minorRadius, centerLine, minorRadius, actualAspectRatio));
434+
416435
return lines;
417436
}
418437
case GridType.RootPhiRectangle:
@@ -422,7 +441,7 @@ public static IEnumerable<Line> CreateGrid(GridType gridType, double width, doub
422441

423442
var w = rectangle.Width / (RatioConstants.RootPhi * RatioConstants.RootPhi);
424443
var h = rectangle.Height / (RatioConstants.RootPhi * RatioConstants.RootPhi);
425-
444+
426445
lines.Add(new Line(rectangle.Left + w, rectangle.Top, rectangle.Left + w, rectangle.Bottom));
427446
lines.Add(new Line(rectangle.Right - w, rectangle.Top, rectangle.Right - w, rectangle.Bottom));
428447
lines.Add(new Line(rectangle.Left, rectangle.Top + h, rectangle.Right, rectangle.Top + h));
@@ -448,6 +467,7 @@ public static IEnumerable<Line> CreateGrid(GridType gridType, double width, doub
448467
lines.Add(new Line(rectangle.Left, rectangle.Top, RatioConstants.Half, rectangle.Bottom));
449468
lines.Add(new Line(RatioConstants.Half, rectangle.Bottom, rectangle.Right, rectangle.Top));
450469
lines.Add(new Line(RatioConstants.Half, rectangle.Top, rectangle.Right, rectangle.Bottom));
470+
451471
return lines;
452472
}
453473
case GridType.Root3Rectangle:
@@ -466,6 +486,7 @@ public static IEnumerable<Line> CreateGrid(GridType gridType, double width, doub
466486
var h = rectangle.Height / 3.0;
467487
lines.Add(new Line(rectangle.Left, rectangle.Top + h, rectangle.Right, rectangle.Top + h));
468488
lines.Add(new Line(rectangle.Left, rectangle.Bottom - h, rectangle.Right, rectangle.Bottom - h));
489+
469490
return lines;
470491
}
471492
case GridType.Root4Rectangle:
@@ -509,6 +530,7 @@ public static IEnumerable<Line> CreateGrid(GridType gridType, double width, doub
509530
lines.Add(new Line(rectangle.Left, rectangle.CenterY, rectangle.Right, rectangle.CenterY));
510531
lines.Add(new Line(rectangle.Left, rectangle.Top + h, rectangle.Right, rectangle.Top + h));
511532
lines.Add(new Line(rectangle.Left, rectangle.Bottom - h, rectangle.Right, rectangle.Bottom - h));
533+
512534
return lines;
513535
}
514536
default:
@@ -518,6 +540,29 @@ public static IEnumerable<Line> CreateGrid(GridType gridType, double width, doub
518540
}
519541
}
520542

543+
private static IList<Line> CreateCirclePoints(double centerX, double centerY, double radius, double actualAspectRatio)
544+
{
545+
const double FullTurn = 2.0 * Math.PI;
546+
var step = 0.01 * FullTurn; // TODO: compute optimal step from circle radius with desired smoothness
547+
548+
var points = new List<Point>((int)(FullTurn / step));
549+
for (var theta = 0.0; theta < FullTurn; theta += step)
550+
{
551+
var x = centerX + radius * Math.Cos(theta);
552+
var y = 1.0 - (centerY + radius * Math.Sin(theta) * actualAspectRatio); // Flip Y axis
553+
554+
points.Add(new Point(x, y));
555+
}
556+
557+
var lines = new List<Line>();
558+
for (var i = 1; i < points.Count; i++)
559+
{
560+
lines.Add(new Line(points[i - 1], points[i]));
561+
}
562+
563+
return lines;
564+
}
565+
521566
private static double CalculateAspectRatio(double width, double height, bool isRotated = false)
522567
{
523568
return ((isRotated) ? (height / width) : (width / height));
@@ -547,6 +592,32 @@ private static Rectangle CalculateDynamicRectangleExtents(double desiredAspectRa
547592
return new Rectangle { X = left, Y = top, Width = right - left, Height = bottom - top };
548593
}
549594

595+
private static IList<Line> CreateRectangle(Rectangle rectangle, bool withMainDiagonals = false)
596+
{
597+
var left = rectangle.Left;
598+
var right = rectangle.Right;
599+
var top = rectangle.Top;
600+
var bottom = rectangle.Bottom;
601+
602+
var lines = new List<Line>()
603+
{
604+
// Four lines making contour
605+
new Line(left, top, left, bottom),
606+
new Line(right, top, right, bottom),
607+
new Line(left, top, right, top),
608+
new Line(left, bottom, right, bottom)
609+
};
610+
611+
// Two main diagonals
612+
if (withMainDiagonals)
613+
{
614+
lines.Add(new Line(left, top, right, bottom));
615+
lines.Add(new Line(left, bottom, right, top));
616+
}
617+
618+
return lines;
619+
}
620+
550621
private static List<Line> CreateRectangleWithMainDiagonals(Rectangle rectangle)
551622
{
552623
double left, right, top, bottom;
@@ -680,7 +751,7 @@ private static IList<Line> CreateRectangleLines(int left, int right, int top, in
680751
});
681752
}
682753

683-
private static IEnumerable<Line> CreateLines(Point[] points)
754+
private static IList<Line> CreateLines(Point[] points)
684755
{
685756
var res = new List<Line>();
686757

Src/ScreenGrid/Models/Grids/GridModeItem.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
namespace ScreenGrid.Models.Grids
22
{
3-
using System;
43
using System.Collections.Generic;
54

65
public class GridModeItem
76
{
8-
private GridType gridType;
7+
private readonly GridType gridType;
98

10-
private string title;
9+
private readonly string title;
1110

1211
public GridModeItem(GridType gridType, string title)
1312
{
@@ -48,6 +47,7 @@ public static IEnumerable<GridModeItem> List
4847
new GridModeItem(GridType.GoldenSpiralZoomed, "Golden Spiral"),
4948
new GridModeItem(GridType.FibonacciRectanglesStretched, "Fibonacci Rectangles (stretched)"),
5049
new GridModeItem(GridType.GoldenSpiralStretched, "Golden Spiral (stretched)"),
50+
new GridModeItem(GridType.GoldenCircles, "Golden Circles"),
5151
new GridModeItem(GridType.OneDotFiveRectangle, "1.5 Rectangle"),
5252
new GridModeItem(GridType.GoldenRectangle, "Golden (Phi) Rectangle"),
5353
new GridModeItem(GridType.RootPhiRectangle, "Root-Phi Rectangle"),

Src/ScreenGrid/Models/Grids/GridType.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ public enum GridType
6565
/// </summary>
6666
GoldenSpiralStretched,
6767

68+
/// <summary>
69+
/// Two pairs of circles
70+
/// </summary>
71+
GoldenCircles,
72+
6873
/// <summary>
6974
/// 1.5 Rectangle (1:1.5 or 3:2)
7075
/// </summary>

Src/ScreenGrid/Models/Grids/RatioConstants.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ static class RatioConstants
1313
public static readonly double Three = 3.0;
1414

1515
/// <summary>
16-
/// 1.618
16+
/// 1.618 (Golden ratio)
1717
/// </summary>
1818
public static readonly double Phi = (1.0 + Math.Sqrt(5.0)) / 2.0;
1919

Src/ScreenGrid/ScreenGrid.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@
131131
<EmbeddedResource Include="Models\Resources\UpperLeftCorner2.png" />
132132
</ItemGroup>
133133
<ItemGroup>
134+
<Page Include="Views\Assets\GridTypes.xaml">
135+
<Generator>MSBuild:Compile</Generator>
136+
<SubType>Designer</SubType>
137+
</Page>
134138
<Page Include="Views\Assets\PathData.xaml">
135139
<Generator>MSBuild:Compile</Generator>
136140
<SubType>Designer</SubType>

Src/ScreenGrid/ViewModels/ScreenGridViewModel.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,22 @@ public ScreenGridViewModel()
3131
this.FlipHorizontalCommand = new RelayCommand((o) => { this.FlipH = !this.FlipH; });
3232
this.FlipVerticalCommand = new RelayCommand((o) => { this.FlipV = !this.FlipV; });
3333
this.SnapCommand = new RelayCommand((o) => { this.SnapToImageBounds(); });
34+
this.SwitchGridSelectorCommand = new RelayCommand((o) => { this.IsGridSelectorVisible = !this.isGridSelectorVisible; });
35+
this.SelectGridCommand = new RelayCommand((o) =>
36+
{
37+
this.GridMode = (GridType)o;
38+
this.IsGridSelectorVisible = false;
39+
// TODO: highlight button (set background color)
40+
});
3441
}
3542

3643
public ICommand RotateClockwiseCommand { get; private set; }
3744
public ICommand RotateCounterClockwiseCommand { get; private set; }
3845
public ICommand FlipHorizontalCommand { get; private set; }
3946
public ICommand FlipVerticalCommand { get; private set; }
4047
public ICommand SnapCommand { get; private set; }
48+
public ICommand SwitchGridSelectorCommand { get; private set; }
49+
public ICommand SelectGridCommand { get; private set; }
4150

4251
private GridType gridMode;
4352
public GridType GridMode
@@ -141,6 +150,33 @@ public ColorItemViewModel SelectedLineColor
141150
}
142151
}
143152

153+
private bool isGridSelectorVisible = false;
154+
155+
public bool IsGridSelectorVisible
156+
{
157+
get
158+
{
159+
return this.isGridSelectorVisible;
160+
}
161+
162+
set
163+
{
164+
if (this.isGridSelectorVisible != value)
165+
{
166+
this.isGridSelectorVisible = value;
167+
base.OnPropertyChanged("GridSelectorVisibility");
168+
}
169+
}
170+
}
171+
172+
public Visibility GridSelectorVisibility
173+
{
174+
get
175+
{
176+
return (this.IsGridSelectorVisible ? Visibility.Visible : Visibility.Collapsed);
177+
}
178+
}
179+
144180
private Brush LineBrush
145181
{
146182
get

0 commit comments

Comments
 (0)