Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
IfsFractal.cs
1using System;
2using System.Collections.Generic;
3using System.Numerics;
4using System.Text;
5using SkiaSharp;
12
13
15{
128 {
134 : base(new ScriptNode[] { xc, yc, dr, N, Transforms, DimX, DimY, Seed },
135 new ArgumentType[] { ArgumentType.Scalar, ArgumentType.Scalar, ArgumentType.Scalar,
136 ArgumentType.Scalar, ArgumentType.Vector, ArgumentType.Scalar, ArgumentType.Scalar,
138 {
139 }
140
146 : base(new ScriptNode[] { xc, yc, dr, N, Transforms, DimX, DimY },
147 new ArgumentType[] { ArgumentType.Scalar, ArgumentType.Scalar, ArgumentType.Scalar,
148 ArgumentType.Scalar, ArgumentType.Vector, ArgumentType.Scalar, ArgumentType.Scalar }, Start, Length, Expression)
149 {
150 }
151
157 : base(new ScriptNode[] { xc, yc, dr, N, Transforms, DimX },
158 new ArgumentType[] { ArgumentType.Scalar, ArgumentType.Scalar, ArgumentType.Scalar,
159 ArgumentType.Scalar, ArgumentType.Vector, ArgumentType.Scalar }, Start, Length, Expression)
160 {
161 }
162
168 : base(new ScriptNode[] { xc, yc, dr, N, Transforms },
169 new ArgumentType[] { ArgumentType.Scalar, ArgumentType.Scalar, ArgumentType.Scalar,
170 ArgumentType.Scalar, ArgumentType.Vector }, Start, Length, Expression)
171 {
172 }
173
177 public override string[] DefaultArgumentNames
178 {
179 get
180 {
181 return new string[] { "xc", "yc", "dr", "N", "Transforms", "DimX", "DimY", "Seed" };
182 }
183 }
184
189 {
190 double xc, yc;
191 double dr;
192 long N;
193 int dimx, dimy;
194 int i, c;
195 int Seed;
196
197 i = 0;
198 c = Arguments.Length;
199 xc = Expression.ToDouble(Arguments[i++].AssociatedObjectValue);
200 yc = Expression.ToDouble(Arguments[i++].AssociatedObjectValue);
201 dr = Expression.ToDouble(Arguments[i++].AssociatedObjectValue);
202 N = (long)Expression.ToDouble(Arguments[i++].AssociatedObjectValue);
203 if (N <= 0)
204 throw new ScriptRuntimeException("N in calls to IfsFractal() must be a positive integer.", this);
205
206 if (!(Arguments[i].AssociatedObjectValue is Array Functions))
207 throw new ScriptRuntimeException("The fifth parameter to IfsFractal must be an array of homogenous 2D-transformations or lambda expressions.", this);
208
209 List<DoubleMatrix> Matrices = null;
210 List<ILambdaExpression> LambdaExpressions = null;
211 List<double> Weights = new List<double>();
212 List<SKColor> Colors = new List<SKColor>();
213 string FunctionsExpression = this.Arguments[i++].SubExpression;
214
215 foreach (object f in Functions)
216 {
217 if (f is DoubleMatrix M)
218 {
219 if (!(LambdaExpressions is null))
220 throw new ScriptRuntimeException("Cannot mix homogenous 2D-transforms with lambda expressions.", this);
221
222 if (Matrices is null)
223 Matrices = new List<DoubleMatrix>();
224
225 Matrices.Add(M);
226 Weights.Add(1);
227 Colors.Add(SKColors.Black);
228 }
229 else if (f is ILambdaExpression Lambda)
230 {
231 if (!(Matrices is null))
232 throw new ScriptRuntimeException("Cannot mix homogenous 2D-transforms with lambda expressions.", this);
233
234 if (LambdaExpressions is null)
235 LambdaExpressions = new List<ILambdaExpression>();
236
237 LambdaExpressions.Add(Lambda);
238 Weights.Add(1);
239 Colors.Add(SKColors.Black);
240 }
241 else if (f is SKColor || f is string)
242 {
243 if (Colors.Count == 0)
244 throw new ScriptRuntimeException("Color definitions can only be specified after each transformation.", this);
245
246 Colors[Colors.Count - 1] = Graph.ToColor(f);
247 }
248 else
249 {
250 try
251 {
252 double d = Expression.ToDouble(f);
253
254 if (Weights.Count == 0)
255 throw new ScriptRuntimeException("Weight definitions can only be specified after each transformation.", this);
256
257 Weights[Weights.Count - 1] = d;
258 }
259 catch (Exception)
260 {
261 throw new ScriptRuntimeException("The fifth parameter to IfsFractal must be an array of homogenous 2D-transformations or lambda expressions, optionally followed by their corresponding weights and/or colors.", this);
262 }
263 }
264 }
265
266 if (i < c)
267 dimx = (int)Expression.ToDouble(Arguments[i++].AssociatedObjectValue);
268 else
269 dimx = 320;
270
271 if (i < c)
272 dimy = (int)Expression.ToDouble(Arguments[i++].AssociatedObjectValue);
273 else
274 dimy = 200;
275
276 if (i < c)
277 Seed = (int)Expression.ToDouble(Arguments[i++].AssociatedObjectValue);
278 else
279 {
280 lock (gen)
281 {
282 Seed = gen.Next();
283 }
284
285 Variables.ConsoleOut?.WriteLine("Seed = " + Seed.ToString(), Variables);
286 }
287
288 if (i < c)
289 {
290 throw new ScriptRuntimeException("Parameter mismatch in call to IfsFractal(xc,yc,dr,N,Transforms[,dimx[,dimy[,Seed]]]).",
291 this);
292 }
293
294 if (dimx <= 0 || dimx > 5000 || dimy <= 0 || dimy > 5000)
295 throw new ScriptRuntimeException("Image size must be within 1x1 to 5000x5000", this);
296
297 if (!(Matrices is null))
298 {
299 return CalcIfs(Variables, xc, yc, dr, N, Matrices.ToArray(), Weights.ToArray(), Colors.ToArray(), dimx, dimy, Seed,
300 this, this.FractalZoomScript, new object[] { dimx, dimy, N, FunctionsExpression, Seed });
301 }
302 else
303 {
304 return CalcIfs(xc, yc, dr, N, LambdaExpressions.ToArray(), Weights.ToArray(), Colors.ToArray(),
305 dimx, dimy, Seed, this, Variables, this.FractalZoomScript,
306 new object[] { dimx, dimy, N, FunctionsExpression, Seed });
307 }
308 }
309
310 private static readonly Random gen = new Random();
311
315 public override string FunctionName => nameof(IfsFractal);
316
317 private string FractalZoomScript(double r, double i, double Size, object State)
318 {
319 object[] Parameters = (object[])State;
320 int DimX = (int)Parameters[0];
321 int DimY = (int)Parameters[1];
322 long N = (long)Parameters[2];
323 string FunctionsExpression = (string)Parameters[3];
324 int Seed = (int)Parameters[4];
325
326 StringBuilder sb = new StringBuilder();
327
328 sb.Append("IfsFractal(");
329 sb.Append(Expression.ToString(r));
330 sb.Append(',');
331 sb.Append(Expression.ToString(i));
332 sb.Append(',');
333 sb.Append(Expression.ToString(Size / 4));
334 sb.Append(',');
335 sb.Append(N.ToString());
336 sb.Append(',');
337 sb.Append(FunctionsExpression);
338 sb.Append(',');
339 sb.Append(DimX.ToString());
340 sb.Append(',');
341 sb.Append(DimY.ToString());
342 sb.Append(',');
343 sb.Append(Seed.ToString());
344 sb.Append(')');
345
346 return sb.ToString();
347 }
348
352 public static FractalGraph CalcIfs(Variables Variables, double xCenter, double yCenter, double rDelta, long N, DoubleMatrix[] Functions,
353 double[] Weights, SKColor[] Colors, int Width, int Height, int Seed, ScriptNode Node, FractalZoomScript FractalZoomScript, object State)
354 {
355 DoubleMatrix M;
356 double[,] E;
357 double[][] Coefficients;
358 double TotWeight = 0;
359 double Weight;
360 SKColor cl;
361 byte[] Reds;
362 byte[] Greens;
363 byte[] Blues;
364 int i, c = Functions.Length;
365 Random Gen = new Random(Seed);
366
367 if (c < 2)
368 throw new ScriptRuntimeException("At least two transformations need to be provided.", Node);
369
370 if (Weights.Length != c)
371 throw new ArgumentException("Weights must be of equal length as Functions.", "Weights");
372
373 if (Colors.Length != c)
374 throw new ArgumentException("Colors must be of equal length as Functions.", "Colors");
375
376 for (i = 0; i < c; i++)
377 {
378 Weight = Weights[i];
379 if (Weight < 0)
380 throw new ScriptRuntimeException("Weights must be non-negative.", Node);
381
382 Weights[i] += TotWeight;
383 TotWeight += Weight;
384 }
385
386 if (TotWeight == 0)
387 throw new ScriptRuntimeException("The total weight of all functions must be postitive.", Node);
388
389 for (i = 0; i < c; i++)
390 Weights[i] /= TotWeight;
391
392 Coefficients = new double[c][];
393 Reds = new byte[c];
394 Greens = new byte[c];
395 Blues = new byte[c];
396
397 for (i = 0; i < c; i++)
398 {
399 cl = Colors[i];
400 Reds[i] = cl.Red;
401 Greens[i] = cl.Green;
402 Blues[i] = cl.Blue;
403
404 M = Functions[i];
405 E = M.Values;
406
407 if (M.Columns == 2 && M.Rows == 2)
408 {
409 Coefficients[i] = new double[]
410 {
411 (double)E[0, 0], (double)E[0, 1], 0,
412 (double)E[1, 0], (double)E[1, 1], 0,
413 0, 0, 1
414 };
415 }
416 else if (M.Columns == 3 && M.Rows == 3)
417 {
418 Coefficients[i] = new double[]
419 {
420 (double)E[0, 0], (double)E[0, 1], (double)E[0, 2],
421 (double)E[1, 0], (double)E[1, 1], (double)E[1, 2],
422 (double)E[2, 0], (double)E[2, 1], (double)E[2, 2]
423 };
424 }
425 else
426 throw new ScriptRuntimeException("Matrix not a linear 2D-transformation or a homogenous 2D-transformation.", Node);
427 }
428
429 int size = Width * Height * 4;
430 byte[] rgb = new byte[size];
431
432 double AspectRatio = ((double)Width) / Height;
433 double x = Gen.NextDouble();
434 double y = Gen.NextDouble();
435 double p = 1;
436 double x2, y2, p2;
437 double[] C;
438 int j;
439 double xMin, xMax, yMin, yMax;
440 double sx, sy;
441 int xi, yi;
442
443 xMin = xCenter - rDelta / 2;
444 xMax = xMin + rDelta;
445 yMin = yCenter - rDelta / (2 * AspectRatio);
446 yMax = yMin + rDelta / AspectRatio;
447
448 sx = Width / (xMax - xMin);
449 sy = Height / (yMax - yMin);
450
451 for (i = 0; i < 20; i++)
452 {
453 Weight = Gen.NextDouble();
454 j = 0;
455 while (j < c - 1 && Weights[j] <= Weight)
456 j++;
457
458 C = Coefficients[j];
459
460 x2 = C[0] * x + C[1] * y + C[2] * p;
461 y2 = C[3] * x + C[4] * y + C[5] * p;
462 p2 = C[6] * x + C[7] * y + C[8] * p;
463
464 x = x2;
465 y = y2;
466 p = p2;
467 }
468
469 while (N-- > 0)
470 {
471 Weight = Gen.NextDouble();
472 j = 0;
473 while (j < c - 1 && Weights[j] <= Weight)
474 j++;
475
476 C = Coefficients[j];
477
478 x2 = C[0] * x + C[1] * y + C[2] * p;
479 y2 = C[3] * x + C[4] * y + C[5] * p;
480 p2 = C[6] * x + C[7] * y + C[8] * p;
481
482 x = x2;
483 y = y2;
484 p = p2;
485
486 if (p == 0)
487 break;
488
489 if (x < xMin || x > xMax || y < yMin || y > yMax)
490 continue;
491
492 xi = (int)((x / p - xMin) * sx + 0.5);
493 yi = Height - 1 - (int)((y / p - yMin) * sy + 0.5);
494
495 if (xi < 0 || xi >= Width || yi < 0 || yi >= Height)
496 continue;
497
498 i = (yi * Width + xi) << 2;
499
500 if (rgb[i + 3] == 0)
501 {
502 rgb[i++] = Blues[j];
503 rgb[i++] = Greens[j];
504 rgb[i++] = Reds[j];
505 rgb[i] = 0xff;
506 }
507 else
508 {
509 rgb[i] = (byte)((rgb[i] + Blues[j]) >> 1);
510 i++;
511 rgb[i] = (byte)((rgb[i] + Greens[j]) >> 1);
512 i++;
513 rgb[i] = (byte)((rgb[i] + Reds[j]) >> 1);
514 }
515 }
516
517 PixelInformation Pixels = new PixelInformationRaw(SKColorType.Bgra8888, rgb, Width, Height, Width << 2);
518 return new FractalGraph(Variables, Pixels, xMin, yMin, xMax, yMax, rDelta, false, Node, FractalZoomScript, State);
519 }
520
524 public static FractalGraph CalcIfs(double xCenter, double yCenter, double rDelta, long N,
525 ILambdaExpression[] Functions, double[] Weights, SKColor[] Colors, int Width, int Height, int Seed,
526 ScriptNode Node, Variables Variables, FractalZoomScript FractalZoomScript, object State)
527 {
528 ILambdaExpression Lambda;
529 double TotWeight = 0;
530 double Weight;
531 bool[] Real;
532 byte[] Reds;
533 byte[] Greens;
534 byte[] Blues;
535 SKColor cl;
536 int i, c = Functions.Length;
537 Random Gen = new Random(Seed);
538
539 if (c < 2)
540 throw new ScriptRuntimeException("At least two transformations need to be provided.", Node);
541
542 if (Weights.Length != c)
543 throw new ArgumentException("Weights must be of equal length as Functions.", "Weights");
544
545 if (Colors.Length != c)
546 throw new ArgumentException("Colors must be of equal length as Functions.", "Colors");
547
548 for (i = 0; i < c; i++)
549 {
550 Weight = Weights[i];
551 if (Weight < 0)
552 throw new ScriptRuntimeException("Weights must be non-negative.", Node);
553
554 Weights[i] += TotWeight;
555 TotWeight += Weight;
556 }
557
558 if (TotWeight == 0)
559 throw new ScriptRuntimeException("The total weight of all functions must be postitive.", Node);
560
561 for (i = 0; i < c; i++)
562 Weights[i] /= TotWeight;
563
564 Real = new bool[c];
565 Reds = new byte[c];
566 Greens = new byte[c];
567 Blues = new byte[c];
568
569 for (i = 0; i < c; i++)
570 {
571 cl = Colors[i];
572 Reds[i] = cl.Red;
573 Greens[i] = cl.Green;
574 Blues[i] = cl.Blue;
575
576 Lambda = Functions[i];
577
578 switch (Lambda.NrArguments)
579 {
580 case 1:
581 Real[i] = false;
582 break;
583
584 case 2:
585 Real[i] = true;
586 break;
587
588 default:
589 throw new ScriptRuntimeException("Lambda expressions in calls to IfsFractal() must be either real-values (taking two parameters) or complex valued (taking one parameter).", Node);
590 }
591 }
592
593 int size = Width * Height * 4;
594 byte[] rgb = new byte[size];
595
596 double AspectRatio = ((double)Width) / Height;
597 double x = Gen.NextDouble();
598 double y = Gen.NextDouble();
599 int j;
600 double xMin, xMax, yMin, yMax;
601 double sx, sy;
602 DoubleNumber xv = new DoubleNumber(0);
603 DoubleNumber yv = new DoubleNumber(0);
604 ComplexNumber zv = new ComplexNumber(0);
605 IElement[] RealParameters = new IElement[] { xv, yv };
606 IElement[] ComplexParameters = new IElement[] { zv };
607 Variables v = new Variables();
608 int xi, yi;
609
610 Variables.CopyTo(v);
611
612 xMin = xCenter - rDelta / 2;
613 xMax = xMin + rDelta;
614 yMin = yCenter - rDelta / (2 * AspectRatio);
615 yMax = yMin + rDelta / AspectRatio;
616
617 sx = Width / (xMax - xMin);
618 sy = Height / (yMax - yMin);
619
620 Complex z;
621
622 for (i = 0; i < 20; i++)
623 {
624 Weight = Gen.NextDouble();
625 j = 0;
626 while (j < c - 1 && Weights[j] <= Weight)
627 j++;
628
629 Lambda = Functions[j];
630
631 if (Real[j])
632 {
633 xv.Value = x;
634 yv.Value = y;
635
636 if (!(Lambda.Evaluate(RealParameters, v) is IVector Result) || Result.Dimension != 2)
637 throw new ScriptRuntimeException("Expected 2-dimensional numeric vector as a result.", Node);
638
639 x = Expression.ToDouble(Result.GetElement(0).AssociatedObjectValue);
640 y = Expression.ToDouble(Result.GetElement(1).AssociatedObjectValue);
641 }
642 else
643 {
644 zv.Value = new Complex(x, y);
645 z = Expression.ToComplex(Lambda.Evaluate(ComplexParameters, v).AssociatedObjectValue);
646
647 x = z.Real;
648 y = z.Imaginary;
649 }
650 }
651
652 while (N-- > 0)
653 {
654 Weight = Gen.NextDouble();
655 j = 0;
656 while (j < c - 1 && Weights[j] <= Weight)
657 j++;
658
659 Lambda = Functions[j];
660
661 if (Real[j])
662 {
663 xv.Value = x;
664 yv.Value = y;
665
666 if (!(Lambda.Evaluate(RealParameters, v) is IVector Result) || Result.Dimension != 2)
667 throw new ScriptRuntimeException("Expected 2-dimensional numeric vector as a result.", Node);
668
669 x = Expression.ToDouble(Result.GetElement(0).AssociatedObjectValue);
670 y = Expression.ToDouble(Result.GetElement(1).AssociatedObjectValue);
671 }
672 else
673 {
674 zv.Value = new Complex(x, y);
675 z = Expression.ToComplex(Lambda.Evaluate(ComplexParameters, v).AssociatedObjectValue);
676
677 x = z.Real;
678 y = z.Imaginary;
679 }
680
681 if (x < xMin || x > xMax || y < yMin || y > yMax)
682 continue;
683
684 xi = (int)((x - xMin) * sx + 0.5);
685 yi = Height - 1 - (int)((y - yMin) * sy + 0.5);
686
687 if (xi < 0 || xi >= Width || yi < 0 || yi >= Height)
688 continue;
689
690 i = (yi * Width + xi) << 2;
691
692 if (rgb[i + 3] == 0)
693 {
694 rgb[i++] = Blues[j];
695 rgb[i++] = Greens[j];
696 rgb[i++] = Reds[j];
697 rgb[i] = 0xff;
698 }
699 else
700 {
701 rgb[i] = (byte)((rgb[i] + Blues[j]) >> 1);
702 i++;
703 rgb[i] = (byte)((rgb[i] + Greens[j]) >> 1);
704 i++;
705 rgb[i] = (byte)((rgb[i] + Reds[j]) >> 1);
706 }
707 }
708
709 PixelInformation Pixels = new PixelInformationRaw(SKColorType.Bgra8888, rgb, Width, Height, Width << 2);
710 return new FractalGraph(Variables, Pixels, xMin, yMin, xMax, yMax, rDelta, false, Node, FractalZoomScript, State);
711 }
712 }
713}
Class managing a script expression.
Definition: Expression.cs:39
static Complex ToComplex(object Object)
Converts an object to a complex value.
Definition: Expression.cs:4942
static double ToDouble(object Object)
Converts an object to a double value.
Definition: Expression.cs:4824
static string ToString(double Value)
Converts a value to a string, that can be parsed as part of an expression.
Definition: Expression.cs:4496
Defines a clickable fractal graph in the complex plane.
Definition: FractalGraph.cs:22
Calculates a fractal based on an Iterated Function System, using the chaos game.
Definition: IfsFractal.cs:128
override IElement Evaluate(IElement[] Arguments, Variables Variables)
TODO
Definition: IfsFractal.cs:188
static FractalGraph CalcIfs(double xCenter, double yCenter, double rDelta, long N, ILambdaExpression[] Functions, double[] Weights, SKColor[] Colors, int Width, int Height, int Seed, ScriptNode Node, Variables Variables, FractalZoomScript FractalZoomScript, object State)
TODO
Definition: IfsFractal.cs:524
IfsFractal(ScriptNode xc, ScriptNode yc, ScriptNode dr, ScriptNode N, ScriptNode Transforms, ScriptNode DimX, ScriptNode DimY, ScriptNode Seed, int Start, int Length, Expression Expression)
TODO
Definition: IfsFractal.cs:132
IfsFractal(ScriptNode xc, ScriptNode yc, ScriptNode dr, ScriptNode N, ScriptNode Transforms, int Start, int Length, Expression Expression)
TODO
Definition: IfsFractal.cs:166
override string FunctionName
TODO
Definition: IfsFractal.cs:315
override string[] DefaultArgumentNames
TODO
Definition: IfsFractal.cs:178
static FractalGraph CalcIfs(Variables Variables, double xCenter, double yCenter, double rDelta, long N, DoubleMatrix[] Functions, double[] Weights, SKColor[] Colors, int Width, int Height, int Seed, ScriptNode Node, FractalZoomScript FractalZoomScript, object State)
TODO
Definition: IfsFractal.cs:352
IfsFractal(ScriptNode xc, ScriptNode yc, ScriptNode dr, ScriptNode N, ScriptNode Transforms, ScriptNode DimX, ScriptNode DimY, int Start, int Length, Expression Expression)
TODO
Definition: IfsFractal.cs:144
IfsFractal(ScriptNode xc, ScriptNode yc, ScriptNode dr, ScriptNode N, ScriptNode Transforms, ScriptNode DimX, int Start, int Length, Expression Expression)
TODO
Definition: IfsFractal.cs:155
Base class for graphs.
Definition: Graph.cs:79
static SKColor ToColor(object Object)
Converts an object to a color.
Definition: Graph.cs:828
Contains pixel information
Contains pixel information in a raw unencoded format.
Base class for multivariate funcions.
ScriptNode[] Arguments
Function arguments.
Base class for all nodes in a parsed script tree.
Definition: ScriptNode.cs:69
int Length
Length of expression covered by node.
Definition: ScriptNode.cs:101
Expression Expression
Expression of which the node is a part.
Definition: ScriptNode.cs:177
int Start
Start position in script expression.
Definition: ScriptNode.cs:92
double[,] Values
Matrix element values.
Definition: DoubleMatrix.cs:56
Collection of variables.
Definition: Variables.cs:25
TextWriter ConsoleOut
Console out interface. Can be used by functions and script to output data to the console.
Definition: Variables.cs:219
void CopyTo(Variables Variables)
Copies available variables to another variable collection.
Definition: Variables.cs:400
Basic interface for all types of elements.
Definition: IElement.cs:20
object AssociatedObjectValue
Associated object value.
Definition: IElement.cs:33
Basic interface for vectors.
Definition: IVector.cs:9
Base interface for lambda expressions.
IElement Evaluate(IElement[] Arguments, Variables Variables)
Evaluates the lambda expression.
delegate string FractalZoomScript(double r, double i, double Size, object State)
Generates new script when zoomed.
ArgumentType
Type of parameter used in a function definition or a lambda definition.
Definition: IFunction.cs:9