@@ -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 [ ]
0 commit comments