Rebol3 Code Examplex


Kronecker product based fractals

Fractals generated by repeatedly taking the Kronecker product of a base matrix with itself, producing self-similar patterns across scales.

Rebol [
    title: "Rosetta code: Kronecker product based fractals"
    file:  %Kronecker_product_based_fractals.r3
    url:   https://rosettacode.org/wiki/Kronecker_product_based_fractals
]

kronecker: context [
    product: function [
        "Kronecker product of two matrices"
        m1 m2
    ][
        count: length? m2
        collect [   
            foreach e m1 [
                counter: 1 
                check: 0
                while [check < count][
                    sub-list: collect [ 
                        foreach n1 e [
                            foreach n2 m2/:counter [keep n1 * n2] 
                        ]
                    ]
                    keep/only sub-list
                    ++ counter
                    ++ check
                ]
            ]
        ]
    ]

    iterate: function [
        "Apply Kronecker product n times"
        matrix [block!] n [integer!]
    ][
        result: matrix
        loop n - 1 [result: product result matrix]
        result
    ]

    render: function [
        "Render the fractal"
        iterations [integer!]
        spec [block! map!]
    ][
        assert [
            block? matrix: spec/matrix
            tuple? color:  spec/color
        ]
        size:  to integer! 3 ** iterations

        matrix: iterate matrix iterations

        rows:  length? matrix
        cols:  length? matrix/1
        scale: to integer! size / cols
        
        img: make image! reduce [as-pair size size 0.0.0.0] ;; transparent image
        
        repeat y rows [
            repeat x cols [
                cm: matrix/:y/:x * color ;; mix color with color with fractional values
                if cm/4 > 0 [            ;; not transparent
                    ;; Draw a filled square or gradual color
                    sy: (y - 1) * scale
                    sx: (x - 1) * scale
                    repeat dy scale [
                        repeat dx scale [
                            pos: (sy + dy - 1) * size + sx + dx
                            if all [pos > 0 pos <= (size * size)][ img/:pos: cm ]
                        ]
                    ]
                ]
            ]
        ]
        img
    ]
]

blend-images: func [
    "Blend images together"
    images [block!] size [pair!]
][
    result: make image! reduce [size black]

    foreach img images [
        repeat i (size/x * size/x) [
            px: img/:i
            if px/4 > 0 [  ;; If not transparent
                c: result/:i
                ;; Simple additive blend with alpha
                c/1: min 255 (c/1 + (px/1 * px/4 / 255))
                c/2: min 255 (c/2 + (px/2 * px/4 / 255))
                c/3: min 255 (c/3 + (px/3 * px/4 / 255))
                result/:i: c
            ]
        ]
    ]
    result
]

generate: function [
    "Generate the fractal"
    iterations
    patterns
][
    layers: copy []
    foreach p patterns [
        append layers img: kronecker/render iterations p
    ]
    blend-images layers img/size
]

img: generate 6 [
    ;; Vicsek
    [   matrix: [[0 1 0]
                 [1 1 1]
                 [0 1 0]]
        color: 255.0.0.255
    ]
    ;; Sierpinski carpet
    [   matrix: [[1 1 1]
                 [1 0 1]
                 [1 1 1]]
        color: 0.0.255.128
    ]
]

try [browse save %Kronecker_product_based_fractals.png img]