Skip to content

Commit de181d6

Browse files
committed
Implemented GridType.GoldenSpiral grid type
1 parent f51d885 commit de181d6

File tree

3 files changed

+94
-51
lines changed

3 files changed

+94
-51
lines changed

Src/ScreenGrid.Models/Grids/GridCreator.cs

Lines changed: 90 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,14 @@ public static Point TransformPoint(
7373

7474
/// <summary>
7575
/// Creates list of lines, which forms the selected grid type
76+
/// X axis is directed from left to right
77+
/// Y axis is directed from top to bottom
78+
/// Coordinates are in normalized range [0..1]
7679
/// </summary>
7780
/// <param name="gridType">Selected type of grid</param>
7881
/// <param name="width">Width of output image</param>
7982
/// <param name="height">Height of output image</param>
80-
/// <returns>List of lines; points coordinates are in normalized range [0..1]</returns>
83+
/// <returns>List of lines</returns>
8184
public static IEnumerable<Line> CreateGrid(GridType gridType, double width, double height)
8285
{
8386
switch (gridType)
@@ -208,6 +211,7 @@ public static IEnumerable<Line> CreateGrid(GridType gridType, double width, doub
208211
break;
209212
}
210213

214+
// TODO: create precalculated (Lazy) array of Fibonnacci numbers
211215
// Update current fibonacci number
212216
{
213217
var temp = current;
@@ -216,76 +220,113 @@ public static IEnumerable<Line> CreateGrid(GridType gridType, double width, doub
216220
}
217221
}
218222

219-
// Scale and auto-fit (stretch all lines to 0..1 output range)
220-
var scaleX = 1.0 / ((double)(right - left));
221-
var scaleY = 1.0 / ((double)(bottom - top));
222-
var offsetX = left;
223-
var offsetY = top;
224-
225-
lines = lines.Select(r => new Line(
226-
new Point((r.p1.X - offsetX) * scaleX, (r.p1.Y - offsetY) * scaleY),
227-
new Point((r.p2.X - offsetX) * scaleX, (r.p2.Y - offsetY) * scaleY)))
228-
.ToList();
223+
lines = StretchToUniformRectangle(lines, left, top, right, bottom);
229224

230225
return lines;
231226
}
232227

233-
////case GridType.GoldenSpiral:
234-
//// {
235-
//// // https://en.wikipedia.org/wiki/Golden_spiral
236-
//// // http://csharphelper.com/blog/2012/05/draw-a-phi-spiral-or-golden-spiral-in-c/
228+
case GridType.GoldenSpiral:
229+
{
230+
// https://en.wikipedia.org/wiki/Golden_spiral
231+
// http://csharphelper.com/blog/2012/05/draw-a-phi-spiral-or-golden-spiral-in-c/
232+
233+
// TODO: Simplify code (use some pre-calculations and common functions)
234+
var points = new List<Point>();
237235

238-
//// var res = new List<Line>();
236+
const double CenterX = 0.5;
237+
const double CenterY = 0.5;
238+
var minX = CenterX;
239+
var minY = CenterY;
240+
var maxX = CenterX;
241+
var maxY = CenterY;
242+
243+
const int NumberOfTurns = 3;
244+
const double FullTurn = 2.0 * Math.PI;
245+
const double MaxAngle = ((double)NumberOfTurns) * FullTurn;
246+
247+
for (var theta = 0.0; theta < MaxAngle; )
248+
{
249+
var r = Math.Pow(RatioConstants.GoldenSpiralCInRadians, theta);
239250

240-
//// // TODO: better algorithm
241-
//// var points = new List<Point>();
251+
var x = CenterX + r * Math.Cos(theta);
252+
var y = 1.0 - (CenterY + r * Math.Sin(theta)); // Flip Y axis
242253

243-
//// for (var theta = 0.0; theta < 3.0 * 2.0 * Math.PI; theta += 0.05 * Math.PI)
244-
//// {
245-
//// var r = 0.001 * Math.Pow(RatioConstants.GoldenSpiralCInRadians, theta);
254+
points.Add(new Point(x, y));
246255

247-
//// var x = 0.5 + r * Math.Cos(theta);
248-
//// var y = 0.5 + r * Math.Sin(theta);
249-
//// points.Add(new Point(x, y));
250-
//// }
256+
minX = Math.Min(minX, x);
257+
minY = Math.Min(minY, y);
258+
maxX = Math.Max(maxX, x);
259+
maxY = Math.Max(maxY, y);
251260

252-
////const int num_slices = 1000;
261+
// variable step for optimization
262+
var turnNumber = theta / FullTurn;
263+
var step = 0.005 * 5.0 * ((double)NumberOfTurns) / (turnNumber + 1.0) * FullTurn;
264+
theta += step;
265+
}
253266

254-
////var start = new Point(0, 1);
255-
////var origin = new Point(0.5, 0.5);
256-
////var dx = start.X - origin.X;
257-
////var dy = start.Y - origin.Y;
258-
////var radius = Math.Sqrt(dx * dx + dy * dy);
259-
////var theta = Math.Atan2(dy, dx);
267+
// Add some points after main turns to finish spiral (inscribe to rectangle)
268+
var lastY = minY;
269+
for (var theta = MaxAngle; ; theta += 0.001 * FullTurn)
270+
{
271+
var r = Math.Pow(RatioConstants.GoldenSpiralCInRadians, theta);
260272

261-
////var dtheta = Math.PI / 2.0 / num_slices;
262-
////var factor = 1 - (1 / RatioConstants.Phi) / num_slices * 0.78;
273+
var x = CenterX + r * Math.Cos(theta);
274+
var y = 1.0 - (CenterY + r * Math.Sin(theta)); // Flip Y axis
263275

264-
////// Repeat until dist is too small to see.
265-
////while (radius > 0.01)
266-
////{
267-
//// points.Add(new Point(
268-
//// (origin.X + radius * Math.Cos(theta)),
269-
//// (origin.Y + radius * Math.Sin(theta))));
276+
points.Add(new Point(x, y));
270277

271-
//// theta += dtheta;
272-
//// radius *= factor;
273-
////}
278+
minX = Math.Min(minX, x);
279+
minY = Math.Min(minY, y);
280+
maxX = Math.Max(maxX, x);
281+
maxY = Math.Max(maxY, y);
274282

275-
//// for (var i = 1; i < points.Count; i++)
276-
//// {
277-
//// res.Add(new Line(points[i - 1], points[i]));
278-
//// }
283+
if (y <= lastY)
284+
{
285+
// TODO: Correct last point (clip to bounds)
286+
break;
287+
}
288+
}
279289

280-
//// return res;
281-
////}
290+
var lines = new List<Line>();
291+
for (var i = 1; i < points.Count; i++)
292+
{
293+
lines.Add(new Line(points[i - 1], points[i]));
294+
}
295+
296+
lines = StretchToUniformRectangle(lines, minX, minY, maxX, maxY);
297+
298+
return lines;
299+
}
282300
default:
283301
{
284302
throw new ArgumentException(gridType.ToString());
285303
}
286304
}
287305
}
288306

307+
/// <summary>
308+
/// Scale and auto-fit (stretch all lines to 0..1 output range) set of lines
309+
/// Min/max values passed as precalculated parameters for optimization
310+
/// </summary>
311+
/// <param name="lines">List of lines to stretch</param>
312+
/// <param name="minX">Minimum X of points of lines</param>
313+
/// <param name="minY">Minimum Y of points of lines</param>
314+
/// <param name="maxX">Maximum X of points of lines</param>
315+
/// <param name="maxY">Maximum Y of points of lines</param>
316+
/// <returns></returns>
317+
private static List<Line> StretchToUniformRectangle(IEnumerable<Line> lines, double minX, double minY, double maxX, double maxY)
318+
{
319+
var scaleX = 1.0 / (maxX - minX);
320+
var scaleY = 1.0 / (maxY - minY);
321+
var offsetX = minX;
322+
var offsetY = minY;
323+
324+
return lines.Select(r => new Line(
325+
new Point((r.p1.X - offsetX) * scaleX, (r.p1.Y - offsetY) * scaleY),
326+
new Point((r.p2.X - offsetX) * scaleX, (r.p2.Y - offsetY) * scaleY)))
327+
.ToList();
328+
}
329+
289330
private static IList<Line> CreateRectangle(int left, int right, int top, int bottom)
290331
{
291332
return new List<Line>(new[]

Src/ScreenGrid.Models/Grids/GridModeItem.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public static IEnumerable<GridModeItem> List
4343
new GridModeItem(GridType.GoldenTriangle, "Golden Triangle" ),
4444
new GridModeItem(GridType.GoldenDiagonal, "Golden Diagonal" ),
4545
new GridModeItem(GridType.FibonacciRectangles, "Fibonacci Rectangles"),
46-
// TODO: new GridModeItem(GridType.GoldenSpiral, "Golden Spiral"),
46+
new GridModeItem(GridType.GoldenSpiral, "Golden Spiral"),
4747
};
4848
}
4949
}

Src/ScreenGrid.Models/Grids/GridType.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public enum GridType
2828

2929
FibonacciRectangles,
3030

31-
// TODO: GoldenSpiral,
31+
GoldenSpiral,
32+
33+
// TODO: Implement normal (golden ratio) / stretched to window grids
3234
}
3335
}

0 commit comments

Comments
 (0)