[skottie] Initial fractal noise effect

Supported options:

  - noise type
  - complexity
  - evolution
  - sub influence
  - sub scaling
  - sub rotation
  - sub offset
  - random seed
  - rotation
  - uniform scaling
  - scale/scale width/scale height
  - offset

TBR=
Change-Id: Id54ace54ec2d474e6d016f51db68c8d4fd229a4c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/374676
Commit-Queue: Florin Malita <fmalita@google.com>
Reviewed-by: Florin Malita <fmalita@google.com>
diff --git a/modules/skottie/skottie.gni b/modules/skottie/skottie.gni
index 5bfde41..beefb49 100644
--- a/modules/skottie/skottie.gni
+++ b/modules/skottie/skottie.gni
@@ -48,6 +48,7 @@
   "$_src/effects/Effects.cpp",
   "$_src/effects/Effects.h",
   "$_src/effects/FillEffect.cpp",
+  "$_src/effects/FractalNoiseEffect.cpp",
   "$_src/effects/GaussianBlurEffect.cpp",
   "$_src/effects/GlowStyles.cpp",
   "$_src/effects/GradientEffect.cpp",
diff --git a/modules/skottie/src/effects/Effects.cpp b/modules/skottie/src/effects/Effects.cpp
index 69dec17..f35f13c 100644
--- a/modules/skottie/src/effects/Effects.cpp
+++ b/modules/skottie/src/effects/Effects.cpp
@@ -38,6 +38,7 @@
         { "ADBE Drop Shadow"            , &EffectBuilder::attachDropShadowEffect         },
         { "ADBE Easy Levels2"           , &EffectBuilder::attachEasyLevelsEffect         },
         { "ADBE Fill"                   , &EffectBuilder::attachFillEffect               },
+        { "ADBE Fractal Noise"          , &EffectBuilder::attachFractalNoiseEffect       },
         { "ADBE Gaussian Blur 2"        , &EffectBuilder::attachGaussianBlurEffect       },
         { "ADBE Geometry2"              , &EffectBuilder::attachTransformEffect          },
         { "ADBE HUE SATURATION"         , &EffectBuilder::attachHueSaturationEffect      },
diff --git a/modules/skottie/src/effects/Effects.h b/modules/skottie/src/effects/Effects.h
index e588c76..d88e4c0 100644
--- a/modules/skottie/src/effects/Effects.h
+++ b/modules/skottie/src/effects/Effects.h
@@ -53,6 +53,8 @@
                                                            sk_sp<sksg::RenderNode>) const;
     sk_sp<sksg::RenderNode> attachFillEffect              (const skjson::ArrayValue&,
                                                            sk_sp<sksg::RenderNode>) const;
+    sk_sp<sksg::RenderNode> attachFractalNoiseEffect      (const skjson::ArrayValue&,
+                                                           sk_sp<sksg::RenderNode>) const;
     sk_sp<sksg::RenderNode> attachGaussianBlurEffect      (const skjson::ArrayValue&,
                                                            sk_sp<sksg::RenderNode>) const;
     sk_sp<sksg::RenderNode> attachGradientEffect          (const skjson::ArrayValue&,
diff --git a/modules/skottie/src/effects/FractalNoiseEffect.cpp b/modules/skottie/src/effects/FractalNoiseEffect.cpp
new file mode 100644
index 0000000..d995a14
--- /dev/null
+++ b/modules/skottie/src/effects/FractalNoiseEffect.cpp
@@ -0,0 +1,408 @@
+/*
+ * Copyright 2021 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "modules/skottie/src/effects/Effects.h"
+
+#include "include/effects/SkRuntimeEffect.h"
+#include "include/utils/SkRandom.h"
+#include "modules/skottie/src/Adapter.h"
+#include "modules/skottie/src/SkottieJson.h"
+#include "modules/skottie/src/SkottieValue.h"
+#include "modules/sksg/include/SkSGRenderNode.h"
+
+namespace skottie::internal {
+namespace {
+
+// An implementation of the ADBE Fractal Noise effect:
+//
+//  - multiple noise sublayers (octaves) are combined using a weighted average
+//  - each layer is subject to a (cumulative) transform, filter and post-sampling options
+//
+// Parameters:
+//
+//   * Noise Type    -- controls noise layer post-sampling filtering
+//                      (Block, Linear, Soft Linear, Spline)
+//   * Fractal Type  -- determines a noise layer post-filtering transformation
+//                      (Basic, Turbulent Smooth, Turbulent Basic, etc)
+//   * Transform     -- offset/scale/rotate the noise effect (local matrix)
+//
+//   * Complexity    -- number of sublayers;
+//                      can be fractional, where the fractional part modulates the last layer
+//   * Evolution     -- controls noise topology in a gradual manner (can be animated for smooth
+//                      noise transitions)
+//   * Sub Influence -- relative amplitude weight for sublayers (cumulative)
+//
+//   * Sub Scaling/Rotation/Offset -- relative scale for sublayers (cumulative)
+//
+//   * Invert        -- invert noise values
+//
+//   * Contrast      -- apply a contrast to the noise result
+//
+//   * Brightness    -- apply a brightness effect to the noise result
+//
+//
+// TODO:
+//   - Fractal Type
+//   - Invert
+//   - Contrast/Brightness
+
+static constexpr char gNoiseEffectSkSL[] =
+    "uniform half3x3 u_submatrix;" // sublayer transform
+
+    "uniform half u_octaves,"      // number of octaves (can be fractional)
+                 "u_persistence,"  // relative octave weight
+                 "u_evolution;"    // evolution/seed
+
+    // Hash based on hash13 (https://www.shadertoy.com/view/4djSRW).
+    "half hash(half3 v) {"
+        "v  = fract(v*0.1031);"
+        "v += dot(v, v.zxy + 31.32);"
+        "return fract((v.x + v.y)*v.z);"
+    "}"
+
+    // The general idea is to compute a coherent hash for two planes in discretized (x,y,e) space,
+    // and interpolate between them.  This yields gradual changes when animating |e| - which is the
+    // desired outcome.
+    "half sample_noise(vec2 xy) {"
+        "xy = floor(xy);"
+
+        "half e_ = floor(u_evolution),"
+             "t  = u_evolution - e_,"
+             "n0 = hash(half3(xy, e_ + 0)),"
+             "n1 = hash(half3(xy, e_ + 1));"
+
+        // Note: Ideally we would use 4 samples (-1, 0, 1, 2) and cubic interpolation for
+        //       better results -- but that's significantly more expensive than lerp.
+
+        "return mix(n0, n1, t);"
+    "}"
+
+    // filter() placeholder
+    "%s"
+
+    // Generate ceil(u_octaves) noise layers and combine based on persistentce and sublayer xform.
+    "half4 main(vec2 xy) {"
+        "half oct = u_octaves," // initial octave count (this is the effective loop counter)
+             "amp = 1,"         // initial layer amplitude
+            "wacc = 0,"         // weight accumulator
+               "n = 0;"         // noise accumulator
+
+        // Constant loop counter chosen to be >= ceil(u_octaves).
+        // The logical counter is actually 'oct'.
+        "for (half i = 0; i < %u; ++i) {"
+            // effective layer weight computed to accommodate fixed loop counters
+            //
+            //   -- for full octaves:              layer amplitude
+            //   -- for fractional octave:         layer amplitude modulated by fractional part
+            //   -- for octaves > ceil(u_octaves): 0
+            //
+            // e.g. for 6 loops and u_octaves = 2.3, this generates the sequence [1,1,.3,0,0]
+            "half w = amp*saturate(oct);"
+
+            "n += w*filter(xy);"
+
+            "wacc += w;"
+            "amp  *= u_persistence;"
+            "oct  -= 1;"
+
+            "xy = (u_submatrix*half3(xy,1)).xy;"
+        "}"
+
+        "n /= wacc;"
+
+        // TODO: fractal functions
+
+        "return half4(n,n,n,1);"
+    "}";
+
+static constexpr char gFilterNearestSkSL[] =
+    "half filter(half2 xy) {"
+        "return sample_noise(xy);"
+    "}";
+
+static constexpr char gFilterLinearSkSL[] =
+    "half filter(half2 xy) {"
+        "xy -= 0.5;"
+
+        "half n00 = sample_noise(xy + half2(0,0)),"
+             "n10 = sample_noise(xy + half2(1,0)),"
+             "n01 = sample_noise(xy + half2(0,1)),"
+             "n11 = sample_noise(xy + half2(1,1));"
+
+        "half2 t = fract(xy);"
+
+        "return mix(mix(n00, n10, t.x), mix(n01, n11, t.x), t.y);"
+    "}";
+
+static constexpr char gFilterSoftLinearSkSL[] =
+    "half filter(half2 xy) {"
+        "xy -= 0.5;"
+
+        "half n00 = sample_noise(xy + half2(0,0)),"
+             "n10 = sample_noise(xy + half2(1,0)),"
+             "n01 = sample_noise(xy + half2(0,1)),"
+             "n11 = sample_noise(xy + half2(1,1));"
+
+        "half2 t = smoothstep(0, 1, fract(xy));"
+
+        "return mix(mix(n00, n10, t.x), mix(n01, n11, t.x), t.y);"
+    "}";
+
+enum class NoiseFilter {
+    kNearest,
+    kLinear,
+    kSoftLinear,
+    // TODO: kSpline?
+};
+
+sk_sp<SkRuntimeEffect> make_noise_effect(unsigned loops, const char* filter) {
+    auto result = SkRuntimeEffect::Make(SkStringPrintf(gNoiseEffectSkSL, filter, loops), {});
+
+    if (0 && !result.effect) {
+        printf("!!! %s\n", result.errorText.c_str());
+    }
+
+    return std::move(result.effect);
+}
+
+template <unsigned LOOPS, NoiseFilter F>
+sk_sp<SkRuntimeEffect> noise_effect() {
+    static constexpr char const* gFilters[] = {
+        gFilterNearestSkSL,
+        gFilterLinearSkSL,
+        gFilterSoftLinearSkSL
+    };
+
+    static const SkRuntimeEffect* effect =
+            make_noise_effect(LOOPS, gFilters[static_cast<size_t>(F)]).release();
+
+    SkASSERT(effect);
+    return sk_ref_sp(effect);
+}
+
+class FractalNoiseNode final : public sksg::CustomRenderNode {
+public:
+    explicit FractalNoiseNode(sk_sp<RenderNode> child) : INHERITED({std::move(child)}) {}
+
+    SG_ATTRIBUTE(Matrix      , SkMatrix   , fMatrix     )
+    SG_ATTRIBUTE(SubMatrix   , SkMatrix   , fSubMatrix  )
+
+    SG_ATTRIBUTE(NoiseFilter , NoiseFilter, fFilter     )
+    SG_ATTRIBUTE(Octaves     , float      , fOctaves    )
+    SG_ATTRIBUTE(Persistence , float      , fPersistence)
+    SG_ATTRIBUTE(Evolution   , float      , fEvolution  )
+
+private:
+    template <NoiseFilter F>
+    sk_sp<SkRuntimeEffect> getEffect() const {
+        // Bin the loop counter based on the number of octaves (range: [1..20]).
+        // Low complexities are common, so we maximize resolution for the low end.
+        if (fOctaves > 8) return noise_effect<20, F>();
+        if (fOctaves > 4) return noise_effect< 8, F>();
+        if (fOctaves > 3) return noise_effect< 4, F>();
+        if (fOctaves > 2) return noise_effect< 3, F>();
+        if (fOctaves > 1) return noise_effect< 2, F>();
+
+        return noise_effect<1, F>();
+    }
+
+    sk_sp<SkRuntimeEffect> getEffect() const {
+        switch (fFilter) {
+            case NoiseFilter::kNearest   : return this->getEffect<NoiseFilter::kNearest>();
+            case NoiseFilter::kLinear    : return this->getEffect<NoiseFilter::kLinear>();
+            case NoiseFilter::kSoftLinear: return this->getEffect<NoiseFilter::kSoftLinear>();
+        }
+        SkUNREACHABLE;
+    }
+
+    sk_sp<SkShader> buildEffectShader() const {
+        SkRuntimeShaderBuilder builder(this->getEffect());
+
+        builder.uniform("u_octaves")     = fOctaves;
+        builder.uniform("u_persistence") = fPersistence;
+        builder.uniform("u_evolution")   = fEvolution;
+        builder.uniform("u_submatrix")   = std::array<float,9>{
+            fSubMatrix.rc(0,0), fSubMatrix.rc(1,0), fSubMatrix.rc(2,0),
+            fSubMatrix.rc(0,1), fSubMatrix.rc(1,1), fSubMatrix.rc(2,1),
+            fSubMatrix.rc(0,2), fSubMatrix.rc(1,2), fSubMatrix.rc(2,2),
+        };
+
+        return builder.makeShader(&fMatrix, false);
+    }
+
+    SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override {
+        const auto& child = this->children()[0];
+        const auto bounds = child->revalidate(nullptr, SkMatrix::I());
+
+        fEffectShader = this->buildEffectShader();
+
+        return bounds;
+    }
+
+    void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
+        const auto& bounds = this->bounds();
+        const auto local_ctx = ScopedRenderContext(canvas, ctx)
+                .setIsolation(bounds, canvas->getTotalMatrix(), true);
+
+        canvas->saveLayer(&bounds, nullptr);
+        this->children()[0]->render(canvas, local_ctx);
+
+        SkPaint effect_paint;
+        effect_paint.setShader(fEffectShader);
+        effect_paint.setBlendMode(SkBlendMode::kSrcIn);
+
+        canvas->drawPaint(effect_paint);
+    }
+
+    const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; } // no hit-testing
+
+    sk_sp<SkShader> fEffectShader;
+
+    SkMatrix    fMatrix,
+                fSubMatrix;
+    NoiseFilter fFilter      = NoiseFilter::kNearest;
+    float       fOctaves     = 1,
+                fPersistence = 1,
+                fEvolution   = 0;
+
+    using INHERITED = sksg::CustomRenderNode;
+};
+
+class FractalNoiseAdapter final : public DiscardableAdapterBase<FractalNoiseAdapter,
+                                                                FractalNoiseNode> {
+public:
+    FractalNoiseAdapter(const skjson::ArrayValue& jprops,
+                        const AnimationBuilder* abuilder,
+                        sk_sp<FractalNoiseNode> node)
+        : INHERITED(std::move(node))
+    {
+        EffectBinder(jprops, *abuilder, this)
+            .bind( 0, fFractalType     )
+            .bind( 1, fNoiseType       )
+            .bind( 2, fInvert          )
+            .bind( 3, fContrast        )
+            .bind( 4, fBrightness      )
+             // 5 -- overflow
+             // 6 -- transform begin-group
+            .bind( 7, fRotation        )
+            .bind( 8, fUniformScaling  )
+            .bind( 9, fScale           )
+            .bind(10, fScaleWidth      )
+            .bind(11, fScaleHeight     )
+            .bind(12, fOffset          )
+             // 13 -- TODO: perspective offset
+             // 14 -- transform end-group
+            .bind(15, fComplexity      )
+             // 16 -- sub settings begin-group
+            .bind(17, fSubInfluence    )
+            .bind(18, fSubScale        )
+            .bind(19, fSubRotation     )
+            .bind(20, fSubOffset       )
+             // 21 -- center subscale
+             // 22 -- sub settings end-group
+            .bind(23, fEvolution       )
+             // 24 -- evolution options begin-group
+             // 25 -- cycle evolution
+             // 26 -- cycle revolution
+            .bind(27, fRandomSeed      )
+             // 28 -- evolution options end-group
+            .bind(29, fOpacity         );
+            // 30 -- TODO: blending mode
+    }
+
+private:
+    float evolution() const {
+        // Constant chosen to visually match AE's evolution rate.
+        const auto evo = SkDegreesToRadians(fEvolution) * 0.25f;
+
+        // The random seed determines an arbitrary start plane.
+        const auto base = SkRandom(static_cast<uint32_t>(fRandomSeed)).nextRangeU(0, 100);
+
+        return evo + base;
+    }
+
+    SkMatrix shaderMatrix() const {
+        static constexpr float kGridSize = 64;
+
+        const auto scale = (SkScalarRoundToInt(fUniformScaling) == 1)
+                ? SkV2{fScale, fScale}
+                : SkV2{fScaleWidth, fScaleHeight};
+
+        return SkMatrix::Translate(fOffset.x, fOffset.y)
+             * SkMatrix::RotateDeg(fRotation)
+             * SkMatrix::Scale(SkTPin(scale.x, 1.0f, 10000.0f) * 0.01f,
+                               SkTPin(scale.y, 1.0f, 10000.0f) * 0.01f)
+             * SkMatrix::Scale(kGridSize, kGridSize);
+    }
+
+    SkMatrix subMatrix() const {
+        const auto scale = 100 / SkTPin(fSubScale, 10.0f, 10000.0f);
+
+        return SkMatrix::Translate(-fSubOffset.x * 0.01f, -fSubOffset.y * 0.01f)
+             * SkMatrix::RotateDeg(-fSubRotation)
+             * SkMatrix::Scale(scale, scale);
+    }
+
+    NoiseFilter noiseFilter() const {
+        switch (SkScalarRoundToInt(fNoiseType)) {
+            case 1:  return NoiseFilter::kNearest;
+            case 2:  return NoiseFilter::kLinear;
+            default: return NoiseFilter::kSoftLinear;
+        }
+        SkUNREACHABLE;
+    }
+
+    void onSync() override {
+        const auto& n = this->node();
+
+        n->setOctaves(SkTPin(fComplexity, 1.0f, 20.0f));
+        n->setPersistence(SkTPin(fSubInfluence * 0.01f, 0.0f, 100.0f));
+        n->setEvolution(this->evolution());
+        n->setNoiseFilter(this->noiseFilter());
+        n->setMatrix(this->shaderMatrix());
+        n->setSubMatrix(this->subMatrix());
+    }
+
+    Vec2Value   fOffset           = {0,0},
+                fSubOffset        = {0,0};
+
+    ScalarValue fFractalType      =     0,
+                fNoiseType        =     0,
+
+                fRotation         =     0,
+                fUniformScaling   =     0,
+                fScale            =   100,  // used when uniform scaling is selected
+                fScaleWidth       =   100,  // used when uniform scaling is not selected
+                fScaleHeight      =   100,  // ^
+
+                fComplexity       =     1,
+                fSubInfluence     =   100,
+                fSubScale         =    50,
+                fSubRotation      =     0,
+
+                fEvolution        =     0,
+                fRandomSeed       =     0,
+
+                fOpacity          =   100, // TODO
+                fInvert           =     0, // TODO
+                fContrast         =   100, // TODO
+                fBrightness       =     0; // TODO
+
+    using INHERITED = DiscardableAdapterBase<FractalNoiseAdapter, FractalNoiseNode>;
+};
+
+} // namespace
+
+sk_sp<sksg::RenderNode> EffectBuilder::attachFractalNoiseEffect(
+        const skjson::ArrayValue& jprops, sk_sp<sksg::RenderNode> layer) const {
+    auto fractal_noise = sk_make_sp<FractalNoiseNode>(std::move(layer));
+
+    return fBuilder->attachDiscardableAdapter<FractalNoiseAdapter>(jprops, fBuilder,
+                                                                   std::move(fractal_noise));
+}
+
+} // namespace skottie::internal
diff --git a/resources/skottie/skottie-fractalnoise-block.json b/resources/skottie/skottie-fractalnoise-block.json
new file mode 100644
index 0000000..be9fa01
--- /dev/null
+++ b/resources/skottie/skottie-fractalnoise-block.json
@@ -0,0 +1 @@
+{"v":"5.7.5","fr":60,"ip":0,"op":601,"w":500,"h":500,"nm":"fractal-block","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":1,"nm":"Medium Green Solid 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Fractal Noise","np":33,"mn":"ADBE Fractal Noise","ix":1,"en":1,"ef":[{"ty":7,"nm":"Fractal Type","mn":"ADBE Fractal Noise-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{"ty":7,"nm":"Noise Type","mn":"ADBE Fractal Noise-0002","ix":2,"v":{"a":0,"k":1,"ix":2}},{"ty":7,"nm":"Invert","mn":"ADBE Fractal Noise-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Contrast","mn":"ADBE Fractal Noise-0004","ix":4,"v":{"a":0,"k":100,"ix":4}},{"ty":0,"nm":"Brightness","mn":"ADBE Fractal Noise-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":7,"nm":"Overflow","mn":"ADBE Fractal Noise-0006","ix":6,"v":{"a":0,"k":4,"ix":6}},{"ty":6,"nm":"Transform","mn":"ADBE Fractal Noise-0007","ix":7,"v":0},{"ty":0,"nm":"Rotation","mn":"ADBE Fractal Noise-0008","ix":8,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":120,"s":[0]},{"t":240,"s":[90]}],"ix":8}},{"ty":7,"nm":"Uniform Scaling","mn":"ADBE Fractal Noise-0009","ix":9,"v":{"a":0,"k":1,"ix":9}},{"ty":0,"nm":"Scale","mn":"ADBE Fractal Noise-0010","ix":10,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":120,"s":[1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":240,"s":[150]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":540,"s":[200]},{"t":600,"s":[1]}],"ix":10}},{"ty":0,"nm":"Scale Width","mn":"ADBE Fractal Noise-0011","ix":11,"v":{"a":0,"k":100,"ix":11}},{"ty":0,"nm":"Scale Height","mn":"ADBE Fractal Noise-0012","ix":12,"v":{"a":0,"k":100,"ix":12}},{"ty":3,"nm":"Offset Turbulence","mn":"ADBE Fractal Noise-0013","ix":13,"v":{"a":0,"k":[250,250],"ix":13}},{"ty":7,"nm":"Perspective Offset","mn":"ADBE Fractal Noise-0031","ix":14,"v":{"a":0,"k":0,"ix":14}},{"ty":6,"nm":"","mn":"ADBE Fractal Noise-0014","ix":15,"v":0},{"ty":0,"nm":"Complexity","mn":"ADBE Fractal Noise-0015","ix":16,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":240,"s":[1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":390,"s":[4]},{"t":600,"s":[1]}],"ix":16}},{"ty":6,"nm":"Sub Settings","mn":"ADBE Fractal Noise-0016","ix":17,"v":0},{"ty":0,"nm":"Sub Influence (%)","mn":"ADBE Fractal Noise-0017","ix":18,"v":{"a":0,"k":100,"ix":18}},{"ty":0,"nm":"Sub Scaling","mn":"ADBE Fractal Noise-0018","ix":19,"v":{"a":0,"k":100,"ix":19}},{"ty":0,"nm":"Sub Rotation","mn":"ADBE Fractal Noise-0019","ix":20,"v":{"a":0,"k":0,"ix":20}},{"ty":3,"nm":"Sub Offset","mn":"ADBE Fractal Noise-0020","ix":21,"v":{"a":0,"k":[25,-25],"ix":21}},{"ty":7,"nm":"Center Subscale","mn":"ADBE Fractal Noise-0021","ix":22,"v":{"a":0,"k":1,"ix":22}},{"ty":6,"nm":"","mn":"ADBE Fractal Noise-0022","ix":23,"v":0},{"ty":0,"nm":"Evolution","mn":"ADBE Fractal Noise-0023","ix":24,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":600,"s":[1440]}],"ix":24}},{"ty":6,"nm":"Evolution Options","mn":"ADBE Fractal Noise-0024","ix":25,"v":0},{"ty":7,"nm":"Cycle Evolution","mn":"ADBE Fractal Noise-0025","ix":26,"v":{"a":0,"k":0,"ix":26}},{"ty":0,"nm":"Cycle (in Revolutions)","mn":"ADBE Fractal Noise-0026","ix":27,"v":{"a":0,"k":1,"ix":27}},{"ty":0,"nm":"Random Seed","mn":"ADBE Fractal Noise-0027","ix":28,"v":{"a":0,"k":0,"ix":28}},{"ty":6,"nm":"Random Seed","mn":"ADBE Fractal Noise-0028","ix":29,"v":0},{"ty":0,"nm":"Opacity","mn":"ADBE Fractal Noise-0029","ix":30,"v":{"a":0,"k":100,"ix":30}},{"ty":7,"nm":"Blending Mode","mn":"ADBE Fractal Noise-0030","ix":31,"v":{"a":0,"k":2,"ix":31}}]}],"sw":500,"sh":500,"sc":"#97f487","ip":0,"op":601,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/resources/skottie/skottie-fractalnoise-linear.json b/resources/skottie/skottie-fractalnoise-linear.json
new file mode 100644
index 0000000..af5a077
--- /dev/null
+++ b/resources/skottie/skottie-fractalnoise-linear.json
@@ -0,0 +1 @@
+{"v":"5.7.5","fr":60,"ip":0,"op":601,"w":500,"h":500,"nm":"fractal-linear","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":1,"nm":"Medium Green Solid 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Fractal Noise","np":33,"mn":"ADBE Fractal Noise","ix":1,"en":1,"ef":[{"ty":7,"nm":"Fractal Type","mn":"ADBE Fractal Noise-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{"ty":7,"nm":"Noise Type","mn":"ADBE Fractal Noise-0002","ix":2,"v":{"a":0,"k":2,"ix":2}},{"ty":7,"nm":"Invert","mn":"ADBE Fractal Noise-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Contrast","mn":"ADBE Fractal Noise-0004","ix":4,"v":{"a":0,"k":100,"ix":4}},{"ty":0,"nm":"Brightness","mn":"ADBE Fractal Noise-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":7,"nm":"Overflow","mn":"ADBE Fractal Noise-0006","ix":6,"v":{"a":0,"k":4,"ix":6}},{"ty":6,"nm":"Transform","mn":"ADBE Fractal Noise-0007","ix":7,"v":0},{"ty":0,"nm":"Rotation","mn":"ADBE Fractal Noise-0008","ix":8,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":120,"s":[0]},{"t":240,"s":[90]}],"ix":8}},{"ty":7,"nm":"Uniform Scaling","mn":"ADBE Fractal Noise-0009","ix":9,"v":{"a":0,"k":1,"ix":9}},{"ty":0,"nm":"Scale","mn":"ADBE Fractal Noise-0010","ix":10,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":120,"s":[1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":240,"s":[150]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":540,"s":[200]},{"t":600,"s":[1]}],"ix":10}},{"ty":0,"nm":"Scale Width","mn":"ADBE Fractal Noise-0011","ix":11,"v":{"a":0,"k":100,"ix":11}},{"ty":0,"nm":"Scale Height","mn":"ADBE Fractal Noise-0012","ix":12,"v":{"a":0,"k":100,"ix":12}},{"ty":3,"nm":"Offset Turbulence","mn":"ADBE Fractal Noise-0013","ix":13,"v":{"a":0,"k":[250,250],"ix":13}},{"ty":7,"nm":"Perspective Offset","mn":"ADBE Fractal Noise-0031","ix":14,"v":{"a":0,"k":0,"ix":14}},{"ty":6,"nm":"","mn":"ADBE Fractal Noise-0014","ix":15,"v":0},{"ty":0,"nm":"Complexity","mn":"ADBE Fractal Noise-0015","ix":16,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":240,"s":[1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":390,"s":[4]},{"t":600,"s":[1]}],"ix":16}},{"ty":6,"nm":"Sub Settings","mn":"ADBE Fractal Noise-0016","ix":17,"v":0},{"ty":0,"nm":"Sub Influence (%)","mn":"ADBE Fractal Noise-0017","ix":18,"v":{"a":0,"k":100,"ix":18}},{"ty":0,"nm":"Sub Scaling","mn":"ADBE Fractal Noise-0018","ix":19,"v":{"a":0,"k":100,"ix":19}},{"ty":0,"nm":"Sub Rotation","mn":"ADBE Fractal Noise-0019","ix":20,"v":{"a":0,"k":0,"ix":20}},{"ty":3,"nm":"Sub Offset","mn":"ADBE Fractal Noise-0020","ix":21,"v":{"a":0,"k":[25,-25],"ix":21}},{"ty":7,"nm":"Center Subscale","mn":"ADBE Fractal Noise-0021","ix":22,"v":{"a":0,"k":1,"ix":22}},{"ty":6,"nm":"","mn":"ADBE Fractal Noise-0022","ix":23,"v":0},{"ty":0,"nm":"Evolution","mn":"ADBE Fractal Noise-0023","ix":24,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":600,"s":[1440]}],"ix":24}},{"ty":6,"nm":"Evolution Options","mn":"ADBE Fractal Noise-0024","ix":25,"v":0},{"ty":7,"nm":"Cycle Evolution","mn":"ADBE Fractal Noise-0025","ix":26,"v":{"a":0,"k":0,"ix":26}},{"ty":0,"nm":"Cycle (in Revolutions)","mn":"ADBE Fractal Noise-0026","ix":27,"v":{"a":0,"k":1,"ix":27}},{"ty":0,"nm":"Random Seed","mn":"ADBE Fractal Noise-0027","ix":28,"v":{"a":0,"k":0,"ix":28}},{"ty":6,"nm":"Random Seed","mn":"ADBE Fractal Noise-0028","ix":29,"v":0},{"ty":0,"nm":"Opacity","mn":"ADBE Fractal Noise-0029","ix":30,"v":{"a":0,"k":100,"ix":30}},{"ty":7,"nm":"Blending Mode","mn":"ADBE Fractal Noise-0030","ix":31,"v":{"a":0,"k":2,"ix":31}}]}],"sw":500,"sh":500,"sc":"#97f487","ip":0,"op":601,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/resources/skottie/skottie-fractalnoise-softlinear.json b/resources/skottie/skottie-fractalnoise-softlinear.json
new file mode 100644
index 0000000..25fc0fa
--- /dev/null
+++ b/resources/skottie/skottie-fractalnoise-softlinear.json
@@ -0,0 +1 @@
+{"v":"5.7.5","fr":60,"ip":0,"op":601,"w":500,"h":500,"nm":"fractal-softlinear","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":1,"nm":"Medium Green Solid 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[250,250,0],"ix":2,"l":2},"a":{"a":0,"k":[250,250,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Fractal Noise","np":33,"mn":"ADBE Fractal Noise","ix":1,"en":1,"ef":[{"ty":7,"nm":"Fractal Type","mn":"ADBE Fractal Noise-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{"ty":7,"nm":"Noise Type","mn":"ADBE Fractal Noise-0002","ix":2,"v":{"a":0,"k":3,"ix":2}},{"ty":7,"nm":"Invert","mn":"ADBE Fractal Noise-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Contrast","mn":"ADBE Fractal Noise-0004","ix":4,"v":{"a":0,"k":100,"ix":4}},{"ty":0,"nm":"Brightness","mn":"ADBE Fractal Noise-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":7,"nm":"Overflow","mn":"ADBE Fractal Noise-0006","ix":6,"v":{"a":0,"k":4,"ix":6}},{"ty":6,"nm":"Transform","mn":"ADBE Fractal Noise-0007","ix":7,"v":0},{"ty":0,"nm":"Rotation","mn":"ADBE Fractal Noise-0008","ix":8,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":120,"s":[0]},{"t":240,"s":[90]}],"ix":8}},{"ty":7,"nm":"Uniform Scaling","mn":"ADBE Fractal Noise-0009","ix":9,"v":{"a":0,"k":1,"ix":9}},{"ty":0,"nm":"Scale","mn":"ADBE Fractal Noise-0010","ix":10,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":120,"s":[1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":240,"s":[150]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":540,"s":[200]},{"t":600,"s":[1]}],"ix":10}},{"ty":0,"nm":"Scale Width","mn":"ADBE Fractal Noise-0011","ix":11,"v":{"a":0,"k":100,"ix":11}},{"ty":0,"nm":"Scale Height","mn":"ADBE Fractal Noise-0012","ix":12,"v":{"a":0,"k":100,"ix":12}},{"ty":3,"nm":"Offset Turbulence","mn":"ADBE Fractal Noise-0013","ix":13,"v":{"a":0,"k":[250,250],"ix":13}},{"ty":7,"nm":"Perspective Offset","mn":"ADBE Fractal Noise-0031","ix":14,"v":{"a":0,"k":0,"ix":14}},{"ty":6,"nm":"","mn":"ADBE Fractal Noise-0014","ix":15,"v":0},{"ty":0,"nm":"Complexity","mn":"ADBE Fractal Noise-0015","ix":16,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":240,"s":[1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":390,"s":[4]},{"t":600,"s":[1]}],"ix":16}},{"ty":6,"nm":"Sub Settings","mn":"ADBE Fractal Noise-0016","ix":17,"v":0},{"ty":0,"nm":"Sub Influence (%)","mn":"ADBE Fractal Noise-0017","ix":18,"v":{"a":0,"k":100,"ix":18}},{"ty":0,"nm":"Sub Scaling","mn":"ADBE Fractal Noise-0018","ix":19,"v":{"a":0,"k":100,"ix":19}},{"ty":0,"nm":"Sub Rotation","mn":"ADBE Fractal Noise-0019","ix":20,"v":{"a":0,"k":0,"ix":20}},{"ty":3,"nm":"Sub Offset","mn":"ADBE Fractal Noise-0020","ix":21,"v":{"a":0,"k":[25,-25],"ix":21}},{"ty":7,"nm":"Center Subscale","mn":"ADBE Fractal Noise-0021","ix":22,"v":{"a":0,"k":1,"ix":22}},{"ty":6,"nm":"","mn":"ADBE Fractal Noise-0022","ix":23,"v":0},{"ty":0,"nm":"Evolution","mn":"ADBE Fractal Noise-0023","ix":24,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":600,"s":[1440]}],"ix":24}},{"ty":6,"nm":"Evolution Options","mn":"ADBE Fractal Noise-0024","ix":25,"v":0},{"ty":7,"nm":"Cycle Evolution","mn":"ADBE Fractal Noise-0025","ix":26,"v":{"a":0,"k":0,"ix":26}},{"ty":0,"nm":"Cycle (in Revolutions)","mn":"ADBE Fractal Noise-0026","ix":27,"v":{"a":0,"k":1,"ix":27}},{"ty":0,"nm":"Random Seed","mn":"ADBE Fractal Noise-0027","ix":28,"v":{"a":0,"k":0,"ix":28}},{"ty":6,"nm":"Random Seed","mn":"ADBE Fractal Noise-0028","ix":29,"v":0},{"ty":0,"nm":"Opacity","mn":"ADBE Fractal Noise-0029","ix":30,"v":{"a":0,"k":100,"ix":30}},{"ty":7,"nm":"Blending Mode","mn":"ADBE Fractal Noise-0030","ix":31,"v":{"a":0,"k":2,"ix":31}}]}],"sw":500,"sh":500,"sc":"#97f487","ip":0,"op":601,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file
diff --git a/resources/skottie/skottie-fractalnoise-suboptions.json b/resources/skottie/skottie-fractalnoise-suboptions.json
new file mode 100644
index 0000000..b3c7cb5
--- /dev/null
+++ b/resources/skottie/skottie-fractalnoise-suboptions.json
@@ -0,0 +1 @@
+{"v":"5.7.5","fr":60,"ip":0,"op":601,"w":500,"h":500,"nm":"fractal-sub-options","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"sub offset","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[125,125,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Fractal Noise","np":33,"mn":"ADBE Fractal Noise","ix":1,"en":1,"ef":[{"ty":7,"nm":"Fractal Type","mn":"ADBE Fractal Noise-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{"ty":7,"nm":"Noise Type","mn":"ADBE Fractal Noise-0002","ix":2,"v":{"a":0,"k":1,"ix":2}},{"ty":7,"nm":"Invert","mn":"ADBE Fractal Noise-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Contrast","mn":"ADBE Fractal Noise-0004","ix":4,"v":{"a":0,"k":100,"ix":4}},{"ty":0,"nm":"Brightness","mn":"ADBE Fractal Noise-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":7,"nm":"Overflow","mn":"ADBE Fractal Noise-0006","ix":6,"v":{"a":0,"k":4,"ix":6}},{"ty":6,"nm":"Transform","mn":"ADBE Fractal Noise-0007","ix":7,"v":0},{"ty":0,"nm":"Rotation","mn":"ADBE Fractal Noise-0008","ix":8,"v":{"a":0,"k":0,"ix":8}},{"ty":7,"nm":"Uniform Scaling","mn":"ADBE Fractal Noise-0009","ix":9,"v":{"a":0,"k":1,"ix":9}},{"ty":0,"nm":"Scale","mn":"ADBE Fractal Noise-0010","ix":10,"v":{"a":0,"k":150,"ix":10}},{"ty":0,"nm":"Scale Width","mn":"ADBE Fractal Noise-0011","ix":11,"v":{"a":0,"k":100,"ix":11}},{"ty":0,"nm":"Scale Height","mn":"ADBE Fractal Noise-0012","ix":12,"v":{"a":0,"k":100,"ix":12}},{"ty":3,"nm":"Offset Turbulence","mn":"ADBE Fractal Noise-0013","ix":13,"v":{"a":0,"k":[250,250],"ix":13}},{"ty":7,"nm":"Perspective Offset","mn":"ADBE Fractal Noise-0031","ix":14,"v":{"a":0,"k":0,"ix":14}},{"ty":6,"nm":"","mn":"ADBE Fractal Noise-0014","ix":15,"v":0},{"ty":0,"nm":"Complexity","mn":"ADBE Fractal Noise-0015","ix":16,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":120,"s":[4]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":540,"s":[4]},{"t":600,"s":[1]}],"ix":16}},{"ty":6,"nm":"Sub Settings","mn":"ADBE Fractal Noise-0016","ix":17,"v":0},{"ty":0,"nm":"Sub Influence (%)","mn":"ADBE Fractal Noise-0017","ix":18,"v":{"a":0,"k":100,"ix":18}},{"ty":0,"nm":"Sub Scaling","mn":"ADBE Fractal Noise-0018","ix":19,"v":{"a":0,"k":100,"ix":19}},{"ty":0,"nm":"Sub Rotation","mn":"ADBE Fractal Noise-0019","ix":20,"v":{"a":0,"k":0,"ix":20}},{"ty":3,"nm":"Sub Offset","mn":"ADBE Fractal Noise-0020","ix":21,"v":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":120,"s":[25,25],"to":[0,0],"ti":[0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":240,"s":[-25,25],"to":[0,0],"ti":[0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":360,"s":[-25,-25],"to":[0,0],"ti":[0,0]},{"t":600,"s":[225,-25]}],"ix":21}},{"ty":7,"nm":"Center Subscale","mn":"ADBE Fractal Noise-0021","ix":22,"v":{"a":0,"k":1,"ix":22}},{"ty":6,"nm":"","mn":"ADBE Fractal Noise-0022","ix":23,"v":0},{"ty":0,"nm":"Evolution","mn":"ADBE Fractal Noise-0023","ix":24,"v":{"a":0,"k":0,"ix":24}},{"ty":6,"nm":"Evolution Options","mn":"ADBE Fractal Noise-0024","ix":25,"v":0},{"ty":7,"nm":"Cycle Evolution","mn":"ADBE Fractal Noise-0025","ix":26,"v":{"a":0,"k":0,"ix":26}},{"ty":0,"nm":"Cycle (in Revolutions)","mn":"ADBE Fractal Noise-0026","ix":27,"v":{"a":0,"k":1,"ix":27}},{"ty":0,"nm":"Random Seed","mn":"ADBE Fractal Noise-0027","ix":28,"v":{"a":0,"k":0,"ix":28}},{"ty":6,"nm":"Random Seed","mn":"ADBE Fractal Noise-0028","ix":29,"v":0},{"ty":0,"nm":"Opacity","mn":"ADBE Fractal Noise-0029","ix":30,"v":{"a":0,"k":100,"ix":30}},{"ty":7,"nm":"Blending Mode","mn":"ADBE Fractal Noise-0030","ix":31,"v":{"a":0,"k":2,"ix":31}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,200],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":601,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"sub influence","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[375,125,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Fractal Noise","np":33,"mn":"ADBE Fractal Noise","ix":1,"en":1,"ef":[{"ty":7,"nm":"Fractal Type","mn":"ADBE Fractal Noise-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{"ty":7,"nm":"Noise Type","mn":"ADBE Fractal Noise-0002","ix":2,"v":{"a":0,"k":1,"ix":2}},{"ty":7,"nm":"Invert","mn":"ADBE Fractal Noise-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Contrast","mn":"ADBE Fractal Noise-0004","ix":4,"v":{"a":0,"k":100,"ix":4}},{"ty":0,"nm":"Brightness","mn":"ADBE Fractal Noise-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":7,"nm":"Overflow","mn":"ADBE Fractal Noise-0006","ix":6,"v":{"a":0,"k":4,"ix":6}},{"ty":6,"nm":"Transform","mn":"ADBE Fractal Noise-0007","ix":7,"v":0},{"ty":0,"nm":"Rotation","mn":"ADBE Fractal Noise-0008","ix":8,"v":{"a":0,"k":0,"ix":8}},{"ty":7,"nm":"Uniform Scaling","mn":"ADBE Fractal Noise-0009","ix":9,"v":{"a":0,"k":1,"ix":9}},{"ty":0,"nm":"Scale","mn":"ADBE Fractal Noise-0010","ix":10,"v":{"a":0,"k":150,"ix":10}},{"ty":0,"nm":"Scale Width","mn":"ADBE Fractal Noise-0011","ix":11,"v":{"a":0,"k":100,"ix":11}},{"ty":0,"nm":"Scale Height","mn":"ADBE Fractal Noise-0012","ix":12,"v":{"a":0,"k":100,"ix":12}},{"ty":3,"nm":"Offset Turbulence","mn":"ADBE Fractal Noise-0013","ix":13,"v":{"a":0,"k":[500,250],"ix":13}},{"ty":7,"nm":"Perspective Offset","mn":"ADBE Fractal Noise-0031","ix":14,"v":{"a":0,"k":0,"ix":14}},{"ty":6,"nm":"","mn":"ADBE Fractal Noise-0014","ix":15,"v":0},{"ty":0,"nm":"Complexity","mn":"ADBE Fractal Noise-0015","ix":16,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":120,"s":[4]},{"t":540,"s":[4]}],"ix":16}},{"ty":6,"nm":"Sub Settings","mn":"ADBE Fractal Noise-0016","ix":17,"v":0},{"ty":0,"nm":"Sub Influence (%)","mn":"ADBE Fractal Noise-0017","ix":18,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":120,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":360,"s":[1000]},{"t":600,"s":[0]}],"ix":18}},{"ty":0,"nm":"Sub Scaling","mn":"ADBE Fractal Noise-0018","ix":19,"v":{"a":0,"k":100,"ix":19}},{"ty":0,"nm":"Sub Rotation","mn":"ADBE Fractal Noise-0019","ix":20,"v":{"a":0,"k":0,"ix":20}},{"ty":3,"nm":"Sub Offset","mn":"ADBE Fractal Noise-0020","ix":21,"v":{"a":0,"k":[25,25],"ix":21}},{"ty":7,"nm":"Center Subscale","mn":"ADBE Fractal Noise-0021","ix":22,"v":{"a":0,"k":1,"ix":22}},{"ty":6,"nm":"","mn":"ADBE Fractal Noise-0022","ix":23,"v":0},{"ty":0,"nm":"Evolution","mn":"ADBE Fractal Noise-0023","ix":24,"v":{"a":0,"k":0,"ix":24}},{"ty":6,"nm":"Evolution Options","mn":"ADBE Fractal Noise-0024","ix":25,"v":0},{"ty":7,"nm":"Cycle Evolution","mn":"ADBE Fractal Noise-0025","ix":26,"v":{"a":0,"k":0,"ix":26}},{"ty":0,"nm":"Cycle (in Revolutions)","mn":"ADBE Fractal Noise-0026","ix":27,"v":{"a":0,"k":1,"ix":27}},{"ty":0,"nm":"Random Seed","mn":"ADBE Fractal Noise-0027","ix":28,"v":{"a":0,"k":0,"ix":28}},{"ty":6,"nm":"Random Seed","mn":"ADBE Fractal Noise-0028","ix":29,"v":0},{"ty":0,"nm":"Opacity","mn":"ADBE Fractal Noise-0029","ix":30,"v":{"a":0,"k":100,"ix":30}},{"ty":7,"nm":"Blending Mode","mn":"ADBE Fractal Noise-0030","ix":31,"v":{"a":0,"k":2,"ix":31}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,200],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":601,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"sub rotation","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[125,375,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Fractal Noise","np":33,"mn":"ADBE Fractal Noise","ix":1,"en":1,"ef":[{"ty":7,"nm":"Fractal Type","mn":"ADBE Fractal Noise-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{"ty":7,"nm":"Noise Type","mn":"ADBE Fractal Noise-0002","ix":2,"v":{"a":0,"k":1,"ix":2}},{"ty":7,"nm":"Invert","mn":"ADBE Fractal Noise-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Contrast","mn":"ADBE Fractal Noise-0004","ix":4,"v":{"a":0,"k":100,"ix":4}},{"ty":0,"nm":"Brightness","mn":"ADBE Fractal Noise-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":7,"nm":"Overflow","mn":"ADBE Fractal Noise-0006","ix":6,"v":{"a":0,"k":4,"ix":6}},{"ty":6,"nm":"Transform","mn":"ADBE Fractal Noise-0007","ix":7,"v":0},{"ty":0,"nm":"Rotation","mn":"ADBE Fractal Noise-0008","ix":8,"v":{"a":0,"k":0,"ix":8}},{"ty":7,"nm":"Uniform Scaling","mn":"ADBE Fractal Noise-0009","ix":9,"v":{"a":0,"k":1,"ix":9}},{"ty":0,"nm":"Scale","mn":"ADBE Fractal Noise-0010","ix":10,"v":{"a":0,"k":150,"ix":10}},{"ty":0,"nm":"Scale Width","mn":"ADBE Fractal Noise-0011","ix":11,"v":{"a":0,"k":100,"ix":11}},{"ty":0,"nm":"Scale Height","mn":"ADBE Fractal Noise-0012","ix":12,"v":{"a":0,"k":100,"ix":12}},{"ty":3,"nm":"Offset Turbulence","mn":"ADBE Fractal Noise-0013","ix":13,"v":{"a":0,"k":[250,500],"ix":13}},{"ty":7,"nm":"Perspective Offset","mn":"ADBE Fractal Noise-0031","ix":14,"v":{"a":0,"k":0,"ix":14}},{"ty":6,"nm":"","mn":"ADBE Fractal Noise-0014","ix":15,"v":0},{"ty":0,"nm":"Complexity","mn":"ADBE Fractal Noise-0015","ix":16,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":120,"s":[4]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":540,"s":[4]},{"t":600,"s":[1]}],"ix":16}},{"ty":6,"nm":"Sub Settings","mn":"ADBE Fractal Noise-0016","ix":17,"v":0},{"ty":0,"nm":"Sub Influence (%)","mn":"ADBE Fractal Noise-0017","ix":18,"v":{"a":0,"k":100,"ix":18}},{"ty":0,"nm":"Sub Scaling","mn":"ADBE Fractal Noise-0018","ix":19,"v":{"a":0,"k":100,"ix":19}},{"ty":0,"nm":"Sub Rotation","mn":"ADBE Fractal Noise-0019","ix":20,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":120,"s":[0]},{"t":600,"s":[360]}],"ix":20}},{"ty":3,"nm":"Sub Offset","mn":"ADBE Fractal Noise-0020","ix":21,"v":{"a":0,"k":[25,25],"ix":21}},{"ty":7,"nm":"Center Subscale","mn":"ADBE Fractal Noise-0021","ix":22,"v":{"a":0,"k":1,"ix":22}},{"ty":6,"nm":"","mn":"ADBE Fractal Noise-0022","ix":23,"v":0},{"ty":0,"nm":"Evolution","mn":"ADBE Fractal Noise-0023","ix":24,"v":{"a":0,"k":0,"ix":24}},{"ty":6,"nm":"Evolution Options","mn":"ADBE Fractal Noise-0024","ix":25,"v":0},{"ty":7,"nm":"Cycle Evolution","mn":"ADBE Fractal Noise-0025","ix":26,"v":{"a":0,"k":0,"ix":26}},{"ty":0,"nm":"Cycle (in Revolutions)","mn":"ADBE Fractal Noise-0026","ix":27,"v":{"a":0,"k":1,"ix":27}},{"ty":0,"nm":"Random Seed","mn":"ADBE Fractal Noise-0027","ix":28,"v":{"a":0,"k":0,"ix":28}},{"ty":6,"nm":"Random Seed","mn":"ADBE Fractal Noise-0028","ix":29,"v":0},{"ty":0,"nm":"Opacity","mn":"ADBE Fractal Noise-0029","ix":30,"v":{"a":0,"k":100,"ix":30}},{"ty":7,"nm":"Blending Mode","mn":"ADBE Fractal Noise-0030","ix":31,"v":{"a":0,"k":2,"ix":31}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,200],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":601,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"sub scale","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[375,375,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ef":[{"ty":5,"nm":"Fractal Noise","np":33,"mn":"ADBE Fractal Noise","ix":1,"en":1,"ef":[{"ty":7,"nm":"Fractal Type","mn":"ADBE Fractal Noise-0001","ix":1,"v":{"a":0,"k":1,"ix":1}},{"ty":7,"nm":"Noise Type","mn":"ADBE Fractal Noise-0002","ix":2,"v":{"a":0,"k":1,"ix":2}},{"ty":7,"nm":"Invert","mn":"ADBE Fractal Noise-0003","ix":3,"v":{"a":0,"k":0,"ix":3}},{"ty":0,"nm":"Contrast","mn":"ADBE Fractal Noise-0004","ix":4,"v":{"a":0,"k":100,"ix":4}},{"ty":0,"nm":"Brightness","mn":"ADBE Fractal Noise-0005","ix":5,"v":{"a":0,"k":0,"ix":5}},{"ty":7,"nm":"Overflow","mn":"ADBE Fractal Noise-0006","ix":6,"v":{"a":0,"k":4,"ix":6}},{"ty":6,"nm":"Transform","mn":"ADBE Fractal Noise-0007","ix":7,"v":0},{"ty":0,"nm":"Rotation","mn":"ADBE Fractal Noise-0008","ix":8,"v":{"a":0,"k":0,"ix":8}},{"ty":7,"nm":"Uniform Scaling","mn":"ADBE Fractal Noise-0009","ix":9,"v":{"a":0,"k":1,"ix":9}},{"ty":0,"nm":"Scale","mn":"ADBE Fractal Noise-0010","ix":10,"v":{"a":0,"k":150,"ix":10}},{"ty":0,"nm":"Scale Width","mn":"ADBE Fractal Noise-0011","ix":11,"v":{"a":0,"k":100,"ix":11}},{"ty":0,"nm":"Scale Height","mn":"ADBE Fractal Noise-0012","ix":12,"v":{"a":0,"k":100,"ix":12}},{"ty":3,"nm":"Offset Turbulence","mn":"ADBE Fractal Noise-0013","ix":13,"v":{"a":0,"k":[500,500],"ix":13}},{"ty":7,"nm":"Perspective Offset","mn":"ADBE Fractal Noise-0031","ix":14,"v":{"a":0,"k":0,"ix":14}},{"ty":6,"nm":"","mn":"ADBE Fractal Noise-0014","ix":15,"v":0},{"ty":0,"nm":"Complexity","mn":"ADBE Fractal Noise-0015","ix":16,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[1]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":120,"s":[4]},{"t":540,"s":[4]}],"ix":16}},{"ty":6,"nm":"Sub Settings","mn":"ADBE Fractal Noise-0016","ix":17,"v":0},{"ty":0,"nm":"Sub Influence (%)","mn":"ADBE Fractal Noise-0017","ix":18,"v":{"a":0,"k":100,"ix":18}},{"ty":0,"nm":"Sub Scaling","mn":"ADBE Fractal Noise-0018","ix":19,"v":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":120,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":241,"s":[200]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":480,"s":[10]},{"t":600,"s":[100]}],"ix":19}},{"ty":0,"nm":"Sub Rotation","mn":"ADBE Fractal Noise-0019","ix":20,"v":{"a":0,"k":0,"ix":20}},{"ty":3,"nm":"Sub Offset","mn":"ADBE Fractal Noise-0020","ix":21,"v":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":480,"s":[25,25],"to":[-4.167,-4.167],"ti":[4.167,4.167]},{"t":600,"s":[0,0]}],"ix":21}},{"ty":7,"nm":"Center Subscale","mn":"ADBE Fractal Noise-0021","ix":22,"v":{"a":0,"k":1,"ix":22}},{"ty":6,"nm":"","mn":"ADBE Fractal Noise-0022","ix":23,"v":0},{"ty":0,"nm":"Evolution","mn":"ADBE Fractal Noise-0023","ix":24,"v":{"a":0,"k":0,"ix":24}},{"ty":6,"nm":"Evolution Options","mn":"ADBE Fractal Noise-0024","ix":25,"v":0},{"ty":7,"nm":"Cycle Evolution","mn":"ADBE Fractal Noise-0025","ix":26,"v":{"a":0,"k":0,"ix":26}},{"ty":0,"nm":"Cycle (in Revolutions)","mn":"ADBE Fractal Noise-0026","ix":27,"v":{"a":0,"k":1,"ix":27}},{"ty":0,"nm":"Random Seed","mn":"ADBE Fractal Noise-0027","ix":28,"v":{"a":0,"k":0,"ix":28}},{"ty":6,"nm":"Random Seed","mn":"ADBE Fractal Noise-0028","ix":29,"v":0},{"ty":0,"nm":"Opacity","mn":"ADBE Fractal Noise-0029","ix":30,"v":{"a":0,"k":100,"ix":30}},{"ty":7,"nm":"Blending Mode","mn":"ADBE Fractal Noise-0030","ix":31,"v":{"a":0,"k":2,"ix":31}}]}],"shapes":[{"ty":"rc","d":1,"s":{"a":0,"k":[200,200],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0,0,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false}],"ip":0,"op":601,"st":0,"bm":0}],"markers":[]}
\ No newline at end of file