Jump to content

Markus-Lyapunov Fractal


Recommended Posts

I might or might not finish this though. I'm working on Markus-Lyapunov as a C# plugin. If I decide not to finish it up, then I'll be leaving this for others to complete.

 

Random option is just a gradient of random color, and double palettes there mean two gradients.

 

Spoiler
// Name: Markus-Lyapunov Fractal
// Submenu: Render
// Author: Reptorian
// Title: Markus-Lyapunov Fractal
// Version: .5
// Desc: Generates Markus-Lyapunov Fractal
// Keywords: Fractal
// URL: https://forums.getpaint.net/profile/85868-reptillian/?ct=1626304395
// Help:
#region UICode
TextboxControl str_var = "ab"; // [255] ABC-String
IntSliderControl abc_repeats = 50; // [1,100] ABC-String Repeats
DoubleSliderControl view_size = 100; // [0.1,100] View Size (%)
PanSliderControl view_pos = Pair.Create(0.000, 0.000); // View Position
DoubleSliderControl c_pos = 0; // [-1,1] C Position
ListBoxControl output_mode = 1; // Mode|Default|Duotone
CheckboxControl revert_mode = false; // Reverted Mode
ColorWheelControl color_a = ColorBgra.FromBgr(255, 0, 0); // [Blue] Color A
ColorWheelControl color_b = ColorBgra.FromBgr(0, 255, 255); // [Yellow] Color B
#endregion
#if DEBUG
#endif
int[] i_var;
int s_str_var;
bool validity;
double[,] Lyapunov_Surface;

double iM;
double im;
double hpi = Math.PI*2;

/*

The C# code below is to use for randomized palette mode. Only kept it here for future development.
double[] hue;
double[] saturation;
double[] intensity;
*/

double contrast(double a){
    double b=a*Math.PI-hpi;
    return .5*(a*a)+.5*(Math.Sin(b)+1)/2;
}

/*

The C# code below is to use for randomized palette mode. Only kept it here for future development.

double rescale(double a,double b){
    double i = a + b ;
    return i * 765;
}

ColorBgra RGB_from_HSI(double hue, double saturation, double intensity){
    int H = ((int)(hue)/60)%6;
    int Z = 1 - Math.Abs((H%2)-1);
    double C = intensity * saturation / (1 + (double)(Z));
    double X = C * (double)(Z);
    double m = intensity * (1 - saturation)/3;
    byte red = 0;
    byte green = 0;
    byte blue = 0;

    switch(H){
        case 0:
         red = (byte)((int)(rescale(C,m)));
         green = (byte)((int)(rescale(X,m)));
         blue = (byte)(rescale(0,m));
         break;
        case 1: 
         red = (byte)((int)(rescale(X,m)));
         green = (byte)((int)(rescale(C,m)));
         blue = (byte)(rescale(0,m));
         break;
        case 2: 
         red = (byte)(rescale(0,m));
         green = (byte)((int)(rescale(C,m)));
         blue = (byte)((int)(rescale(X,m)));
         break;
        case 3: 
         red = (byte)(rescale(0,m));
         green = (byte)((int)(rescale(X,m)));
         blue = (byte)((int)(rescale(C,m)));
         break;
        case 4: 
         red = (byte)((int)(rescale(C,m)));
         green = (byte)(rescale(0,m));
         blue = (byte)((int)(rescale(X,m)));
         break;
        case 5: 
         red = (byte)((int)(rescale(X,m)));
         green = (byte)(rescale(0,m));
         blue = (byte)((int)(rescale(C,m)));
         break;
    }

    return ColorBgra.FromBgr(blue,green,red);
}
*/

double lerp(double a, double b, double t){
    return a * (1 - t) + b * t ;
}

void PreRender(Surface dst, Surface src){

    int w = src.Width;
    int h = src.Height;
    int inc_w = w + 1;
    int inc_h = h + 1;

    if (Lyapunov_Surface == null){Lyapunov_Surface = new double [w,h];}
    else {Array.Clear(Lyapunov_Surface, 0, w*h);}

    s_str_var = str_var.Length;
    char[] charArr=str_var.ToCharArray();
    i_var = new int[s_str_var];
    char temp_char;
    validity = true;

    bool a_valid = false ;
    bool b_valid = false ;

    if (s_str_var==0){
        validity = false;
        return;
    }

    for(int pos = 0 ; pos < s_str_var ; pos++){
        temp_char=Char.ToLower(charArr[pos]);
        i_var[pos]=(int) charArr[pos] - 97;
        if (i_var[pos]<0||i_var[pos]>2){
            validity = false;
            break;
        }
        else{
            switch(i_var[pos]){
                case 0:
                    a_valid = true;
                    break;
                case 1:
                    b_valid = true;
                    break;
                default:
                    break;
            }
        }
    }

    if (!a_valid||!b_valid){
        validity = false;
        return;
    }

    float sqr_size = (float)(view_size /100);
    float gap = 1 - sqr_size ;
    float px = (float)((view_pos.First + 1) / 2);
    float py = (float)((view_pos.Second*-1 + 1) / 2);
    px*=gap;
    py*=gap;
    float lx = px * 2 + 2;
    float ly = py * 2 + 2;
    float rx = (px + sqr_size) * 2 + 2;
    float ry = (py + sqr_size) * 2 + 2;
    double md = Math.Max(w,h);
    double iz = lerp(2+1/(md+1),2+(md/(md+1))*2,(float)(c_pos));

    int total_string_length = s_str_var  * abc_repeats;
    int sp;

    double cx,cy,ix,iy,vn,limit,rn;

    im=0;
    iM=0;

    float f_inc_w = (float)(inc_w+1);
    float f_inc_h = (float)(inc_h+1);

    for(int x = 0 ; x < w ; x++){
        for(int y = 0 ; y < h ; y++){
            cx = (double)(x + 1);
            cy = (double)(y + 1);
            ix = lerp(lx,rx,cx/f_inc_w);
            iy = lerp(ry,ly,cy/f_inc_h);
            vn = .5f;
            limit = 0;
            for (int n = 0 ; n < total_string_length ; n++){
                sp = n % s_str_var;
                sp = i_var[sp];
                switch(sp){
                    case 0:rn=ix;break;
                    case 1:rn=iy;break;
                    case 2:rn=iz;break;
                    default:rn=1;break;
                }
                vn=rn*vn*(1-vn);
                limit+=(float)(Math.Log(Math.Abs((double)(rn*(1-2*vn)))));
            }
            if(limit < im) {im = limit;}
            if(limit > iM) {iM = limit;}
            Lyapunov_Surface[x,y] = limit;
        }
    }
}

void Render(Surface dst, Surface src, Rectangle rect)
{
    // Delete any of these lines you don't need
    int thick = src.Width / s_str_var;
    int represent;
    byte r;

    double tv,shade,t_shade;
    double rescale_factor = (iM-im)/255f;

    double d_r_a = (double)(color_a[2]);
    double d_g_a = (double)(color_a[1]);
    double d_b_a = (double)(color_a[0]);

    double d_r_b = (double)(color_b[2]);
    double d_g_b = (double)(color_b[1]);
    double d_b_b = (double)(color_b[0]);

    double nr,ng,nb;

    if (validity){
        rescale_factor = (iM-im)/255f;
    }
    else{
        rescale_factor = 1;
    }

    for (int y = rect.Top; y < rect.Bottom; y++)
    {
        if (IsCancelRequested) return;
        for (int x = rect.Left; x < rect.Right; x++)
        {
            switch(output_mode){
                case 0:
                    tv = (Lyapunov_Surface[x,y]-im)/(rescale_factor);
                    if (revert_mode){
                        represent = 255 -(int)(tv);
                    }
                    else{
                        represent = (int)(tv);
                    }
                    r = (byte)(represent);
                    dst[x,y] = ColorBgra.FromBgraClamped(r,r,r,255);
                    
                    break;
                case 1:
                    if(Lyapunov_Surface[x,y] >= 0){
                        shade = Lyapunov_Surface[x,y] / iM;
                        nr = d_r_a * shade;
                        ng = d_g_a * shade;
                        nb = d_b_a * shade;
                    }
                    else{
                        shade = Math.Pow(1 - Lyapunov_Surface[x,y] / im,3);
                        nr = d_r_b * shade;
                        ng = d_g_b * shade;
                        nb = d_b_b * shade;
                    }

                    dst[x,y] = ColorBgra.FromBgraClamped((byte)((int)(nb)),(byte)((int)(ng)),(byte)((int)(nr)),255);
                    break;
            }
        }
    }
}

 

 

Edited by Reptillian

G'MIC Filter Developer

Link to comment
Share on other sites

I had just updated code above. Even left some code sample to be used for HSI palette being the base for coloring the Lyapunov Fractal. Though, just from this, I find that the code is really slow. I know this is because that it is single-threaded. Doesn't look like there's much I can do about certain issues besides going into making it a gmic-pdn plugin though I prefer to avoid that route unless there's not a choice.

 

Issues:

1. Slow speed because of it being single-threaded.

2. Lack of random HSI palette mapping.

3. The C percentage gui element cannot be grayed out if there is no 'c' in abc-string.

4. Lack of anti-aliasing.

 

1,2,and 4 is solved by the gmic-pdn route. 3 isn't solve-able though @midora might have an answer to that via option-based library.

Edited by Reptillian

G'MIC Filter Developer

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...