PLOT = "ALL"; // [ALL,FLAPS,FLAP_A,FLAP_B,FLAP_C,SPIKES,SPIKE_A,SPIKE_B,SPIKE_C,SPIKE_D,SKULL]
MODE = "PREVIW"; // [PREVIW,PLOT]

// $fs = (MODE == "PLOT") ? 0.01 : 1;
$fn = (MODE == "PLOT") ? 35 : 10;
// $fn = 0;

SKIRT_D_BIG = 1100;
SKIRT_FLAP_NUM = 12;
SKIRT_FLAP_THIKNES = 0.25;
SKIRT_FLAP_SLANT_NUMBER = 0.0; // 0 is strait; higer is wider below; lower slimmer below
SKIRT_FLAP_LENTH = 400;
SKIRT_FLAP_SPIKE_NUM = 4;
SKIRT_FLAP_SPIKE_BASE_D_UPPER = 20;
SKIRT_FLAP_SPIKE_BASE_D_LOWER = 30;
SKIRT_FLAP_SPIKE_LENGTH_UPPER = 25;
SKIRT_FLAP_SPIKE_LENGTH_LOWER = 37;

SKIRT_FLAP_SPIKE_SEPERATION = SKIRT_FLAP_LENTH/(SKIRT_FLAP_SPIKE_NUM+1);

SPIKE_STEP_COUNT = (MODE == "PLOT") ? 25 : 5;

include <perlin_noise.scad>
include <heat_set_inserts.scad>

// =================================================
// === flap ========================================
// =================================================

module skirt_flap_part(endpart, count = 1) {
    difference() {
        union() {
            linear_extrude(height = SKIRT_FLAP_THIKNES)
            hull() {
                skirt_base_d = SKIRT_D_BIG/SKIRT_FLAP_NUM;
                circle(d=skirt_base_d+(skirt_base_d*SKIRT_FLAP_SLANT_NUMBER*count));
                translate([SKIRT_FLAP_SPIKE_SEPERATION, 0])
                    circle(d=skirt_base_d+(skirt_base_d*SKIRT_FLAP_SLANT_NUMBER*(count+1)));
            }

            cylinder(h = 1, r1 = 15, r2 = 7);
            if (endpart)
            translate([SKIRT_FLAP_SPIKE_SEPERATION, 0])
                cylinder(h = 1, r1 = 15, r2 = 7);
        }
        cylinder(h = 100, d = 3.2);
        translate([SKIRT_FLAP_SPIKE_SEPERATION, 0])
            cylinder(h = 100, d = 3.2);
    }
}

module flap_a() {
    skirt_flap_part(endpart = false, count = 1);
}

module flap_b() {
    skirt_flap_part(endpart = false, count = 2);
}

module flap_c() {
    skirt_flap_part(endpart = true, count = 3);
}

module flap_full() {
    flap_a();
    translate([SKIRT_FLAP_SPIKE_SEPERATION, 0, SKIRT_FLAP_THIKNES+0.1]) 
        flap_b();
    translate([SKIRT_FLAP_SPIKE_SEPERATION, 0, SKIRT_FLAP_THIKNES+0.1]*2) 
        flap_c();
}

// =================================================
// === point =======================================
// =================================================

function point_r_fn(x) = 1-(0.8*x+0.2)^3;
function point_sheer_x_fn(x) = 0.3-(1.6*x)^3;
function point_sheer_y_fn(x) = max(0, x-0.3);
function add(list, c = 0) = c < len(list) - 1 ? list[c] + addl(list, c + 1) : list[c];
function addl(list, l, c = 0) = c < l ? list[c] + addl(list, l, c + 1) : list[c];

module spike(size, f_d, f_sx, f_sy, step)
{
    union() {
        rotate([0, 0, -90])
        difference() {
            union() {
                scale(size)
                for (pos = [0:step:1.5]) {
                    i = round(pos/step);
                    move_x = (i == 0) ? 0 : addl(f_sx, i-1) * step;
                    move_y = (i == 0) ? 0 : addl(f_sy, i-1) * step;
                    if (f_d[i] >= 0)
                    translate([move_x, move_y, pos])
                    multmatrix(m = [
                        [1, 0, f_sx[i], 0],
                        [0, 1, f_sy[i], 0],
                        [0, 0,       1, 0],
                        [0, 0,       0, 1],
                    ]) 
                        cylinder(h = step, r1 = f_d[i], r2 = f_d[i+1]);
                }
            }
            // cavaty for fabric
            translate([0, 0, -0.01]) // remove z bending
            difference() {
                scale(size)
                    cylinder(h = 0.1, r = .85);
                cylinder(h = 0.5*size[2], r1 = 7, r2 = 7+0.4*size[2]);
            }

            // screw hole
            translate([0, 0, -0.01]) { // remove z bending
                // heatSetInsert_hole_M3x5_7();
                cylinder(h = 6.7, d = 4.0);
                translate([0, 0, 6.699])
                    cylinder(h = 0.8, r1 = 2.0, r2 = 1.6);
                cylinder(h = 13, d = 3.2);
            }
        }
    }
}

module spike_noisy(size) {
    noise = [
        perlin_noise(no=4, seed=rands(0, 100000, 1)[0]),
        perlin_noise(no=4, seed=rands(0, 100000, 1)[0]),
        perlin_noise(no=4, seed=rands(0, 100000, 1)[0])
    ];

    STEP = 1 / SPIKE_STEP_COUNT;
    rotate([0, 0, -90])
    spike(
        size,
        // [1, 1, 1],
        [ for (i = [0:STEP:1.5]) point_r_fn(i) + noise[0][round(i*STEP)]*0.2 ],
        [ for (i = [0:STEP:1.5]) point_sheer_x_fn(i) + noise[1][round(i*STEP)]*0.2 ],
        [ for (i = [0:STEP:1.5]) point_sheer_y_fn(i) * (noise[2][round(i*STEP)]*4-2) ],
        STEP
    );
}

module spike_a() { spike_noisy([20, 20, 50]); }
module spike_b() { spike_noisy([17.5, 17.5, 43]); }
module spike_c() { spike_noisy([16, 16, 38]); }
module spike_d() { spike_noisy([14, 14, 35]); }

module spike_full() {
    spike_a();
    translate([SKIRT_FLAP_SPIKE_SEPERATION, 0, SKIRT_FLAP_THIKNES+0.1])
        spike_b();
    translate([SKIRT_FLAP_SPIKE_SEPERATION, 0, SKIRT_FLAP_THIKNES+0.1]*2)
        spike_c();
    translate([SKIRT_FLAP_SPIKE_SEPERATION, 0, SKIRT_FLAP_THIKNES+0.1]*3)
        spike_d();
}


// =================================================
// === skull =======================================
// =================================================

function cubic_bezier_curve(p, n, z) =
    [ for (t = [0:1/(n-1):1])
        [
            ((1-t)^3 * p[0][0]+3*(1-t)^2*t * p[1][0]+3*(1-t)*t^2 * p[2][0]+t^3 * p[3][0]),
            ((1-t)^3 * p[0][1]+3*(1-t)^2*t * p[1][1]+3*(1-t)*t^2 * p[2][1]+t^3 * p[3][1]),
            z
        ]
    ];


module skull(path, curve_points = 10)
{
    points = [
        [0, 0, 0],
        [0, 0, len(path)-1],
        for (h = [0:len(path)-1])
            each for (p = [0:3:len(path[h])-2])
                cubic_bezier_curve(p = [for (i=[p:p+3]) path[h][i]], n = curve_points, z = h)
    ];
    // echo(points);
    ll = round((len(path[0])-1)/3*curve_points);
    faces = [
        for (p = [0:1:ll])
            [0, p, p+1]
        ,
        for (h = [0:len(path)-2])
            each for (p = [1:1:ll])
                [ [h*ll+p+1, h*ll+p, (h+1)*ll+p], [(h+1)*ll+p, (h+1)*ll+p+1, h*ll+p+1] ]
        ,
        for (p = [0:1:ll])
            [1, (len(path)-1)*ll+p, (len(path)-1)*ll+p+1]
    ];
    // echo(faces);
    polyhedron(points = points, faces = faces);
}

function circle_path(r=[[1, 1], [1, 1], [1, 1]], p=90, d=45) =
    [
        [r[0][0], 0],
        for (a = [p:p:360])
            each [ 
                [r[1][0]*cos(a-p+d), r[1][1]*sin(a-p+d)],
                [r[2][0]*cos(a-d)  , r[2][1]*sin(a-d)],
                [r[0][0]*cos(a)    , r[0][1]*sin(a)]
            ]
    ];

function skull_rx(h) = cos(asin(h*2-1))/2;
function skull_ry(h) = cos(asin(h*2-1));

module skull_a()
{
    step = 0.1;
    scale([1, 1, step])
    skull(
        path = [
            for (r = [0:step:1])
                circle_path(
                    r=[
                        [skull_rx(r),               skull_ry(r)],
                        [skull_rx(r)*1.14237401488, skull_ry(r)*1.14237401488],
                        [skull_rx(r)*1.14237401488, skull_ry(r)*1.14237401488]
                    ],
                    p=90,
                    d=28.9111986305
                )
        ],
        curve_points = 1/step
    );
}

// =================================================
// === plot ========================================
// =================================================

if (PLOT == "ALL")
{
    rotate([180, 0, 0])
        flap_full();
    spike_full();
}
else if (PLOT == "FLAPS")
{
    flap_full();
}
else if (PLOT == "FLAP_A")
{
    flap_a();
}
else if (PLOT == "FLAP_B")
{
    flap_b();
}
else if (PLOT == "FLAP_C")
{
    flap_c();
}
else if (PLOT == "SPIKES")
{
    spike_full();
}
else if (PLOT == "SPIKE_A")
{
    spike_a();
}
else if (PLOT == "SPIKE_B")
{
    spike_b();
}
else if (PLOT == "SPIKE_C")
{
    spike_c();
}
else if (PLOT == "SPIKE_D")
{
    spike_d();
}
else if (PLOT == "SKULL")
{
    skull_a();
}
