CG Library SkiaSharp「マンデルブロート集合」

::: note
- 下記サイトにあるWinForms+GDI版マンデルブロート集合をWPF+SkiaSharpに移植しました。作者許諾済み。
:::
 
https://lets-csharp.com/

https://lets-csharp.com/drow-mandelbrot-set/
<hr>

* マウスの左で拡大します。右で縮小です。表示には5秒程掛かります。
* ウィンドウのリサイズは可能ですがかなり動作が遅いです。
* 移植が不完全なのか拡大時に違う所を拡大してしまう事があります。
* 結構古く、DOSの時代に既にありました。

・うまくクリックすると多様な模様が出る(自己相似性)
![1 - コピー (4).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821882/f42e6742-49b8-a144-b993-b4f838215dc0.jpeg)


![1 - コピー (7).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821882/41e23e5a-89cb-598c-ab0a-20b4f628b649.jpeg)


![1 - コピー (6).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821882/18ff12d4-816a-f4bc-c56a-a1dc0b7451cf.jpeg)

## WPF C#

```c#
////////////////////////////////////////////////////////
// WPF+SkiaSharp  マンデルブロ集合
// 移植元 https://lets-csharp.com/drow-mandelbrot-set/
/////////////////////////////////////////////////////////

using SkiaSharp;
using SkiaSharp.Views.Desktop;
using System.Numerics;
using System.Windows;
using System.Windows.Input;

namespace SkiaSharpSample {

    public partial class MainWindow : Window	{
    
        double ParDot = 0.005;
        double CenterX = -0.5;
        double CenterY = 0;

        int R,G,B;
        int NumberOfTrials = 40;

		public MainWindow()		{
			InitializeComponent();
        }

        static int SK_X,SK_Y;

        double RealMin{
            get { return CenterX - ParDot * SK_X / 2; }
        }
 
        double ImaginarylMin{
            get { return CenterY - ParDot * SK_Y / 2; }
        }

		void PaintSurface(object sender, SKPaintSurfaceEventArgs args){
            
           // SKImageInfo info = args.Info;
            SKSurface surface = args.Surface;
            SKCanvas canvas = surface.Canvas;

            SK_X=args.Info.Width;
            SK_Y=args.Info.Height;

            canvas.Clear(SKColors.White);
            
            SKPaint skPaint = new SKPaint{
	            Style = SKPaintStyle.Stroke,
	            IsAntialias = true,
                Color=new SKColor(0,0,0),
	            StrokeWidth = 1               
            };
    
            for (int x = 0; x < SK_X; x++){
                for (int y = 0; y < SK_Y; y++)            {
 
                    Complex c = new Complex(RealMin + x * ParDot,ImaginarylMin + y * ParDot);

                    int count = 0;
                    if (IsMandelbrotSet(c,ref count)){
                        skPaint.Color=new SKColor(255,255,255);
                        canvas.DrawPoint(x, y, skPaint);
                    }
                    else{
                        CreateColorFromInt(count);
                        skPaint.Color=new SKColor((byte)R,(byte)G,(byte)B);
                        canvas.DrawPoint(x, y, skPaint);
                    }
                }
            }
        }

        bool IsMandelbrotSet(Complex c,ref int count){

            int numberOfTrials = 384;
            Complex z = Complex.Zero;
            for (int i = 0; i < numberOfTrials; i++){
                z = z * z + c;
                if (z.Magnitude > 2){
                    count = i;
                    return false;
                }
            }
            count = -1;
            return true;
        }

        private void Window_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) {

            System.Windows.Point point = e.GetPosition(this);
            int MY=(int)point.Y;
            int MX=(int)point.X;
                      
            CenterX = MX*  ParDot + RealMin;
            CenterY = (SK_Y - MY) * ParDot + ImaginarylMin;
            
            if (e.LeftButton == MouseButtonState.Pressed){
                 ParDot *= 0.5;
            }

            if (e.RightButton == MouseButtonState.Pressed){
                 ParDot /= 0.5;
            }
           
            skiaCanvas.InvalidateVisual(); 
        }

        private void skiaCanvas_PreviewMouseMove(object sender, MouseEventArgs e) {
            var x2=e.GetPosition(this).X;
            var y2=e.GetPosition(this).Y;
            
            double x = RealMin - x2 * ParDot;
            double y = ImaginarylMin + (SK_Y - y2) * ParDot;
       
            Title ="X=" + x.ToString()+" Y="+y.ToString() +" ParDot="+ParDot.ToString();        
        }

        void  CreateColorFromInt(int i){
            if (i < 128) {
                R=(i * 2);
                G= i * 2;
                B= 255 - i * 2;
                
            }
            else if (i < 256){
                R=255;
                G=255 - (i - 128) * 2;
                B=0;
            }
            else if (i < 384){
                R=255;
                G=(i - 256) * 2;
                B=(i - 256) * 2;
            }
            else{
                R=0;
                G=0;
                B=0;
            }
        }
    }
}
```


## WPF XAML

```xml
<Window x:Class="SkiaSharpSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:SkiaSharpSample" xmlns:skia="clr-namespace:SkiaSharp.Views.WPF;assembly=SkiaSharp.Views.WPF" 
        xmlns:wpf="clr-namespace:SkiaSharp.Views.WPF;assembly=SkiaSharp.Views.WPF" mc:Ignorable="d"
        Title="SkiaSharp" Height="480" Width="640"  MouseDown="Window_MouseDown"  >

    <Grid  >
        <wpf:SKElement x:Name="skiaCanvas" PaintSurface="PaintSurface"  PreviewMouseMove="skiaCanvas_PreviewMouseMove"  />
    </Grid>
</Window>
```