// --- CONFIGURATION ---

// Outer diameter of the ball (mm)
outer_diameter = 40; // [30:1:100]

// Inner diameter (mm)
inner_diameter = 36; // [20:1:90]

// Width of the bars (mm)
bar_width = 2.0; // [1:0.1:10]

// Radius of the fillet roundings (mm)
fillet_radius = 1.0; // [0:0.1:5]

// Fast Render Mode: If true, skips the spherical cropping (bars have flat tops). Set to false for final STL export if you want curved tops.
fast_render = false; 

// Resolution (higher = smoother)
$fn = 40; // [20:10:100]

// --- GEOMETRY GENERATION ---

R_out = outer_diameter / 2;
R_in = inner_diameter / 2;

// We generate the structure slightly oversized to ensure clean cuts with the shell
R_struct_out = fast_render ? R_out : R_out * 1.1;
R_struct_in = fast_render ? R_in : R_in * 0.9;

phi = 1.61803398875;

// 1. Icosahedron Vertices (Basis)
iso_verts = [
    [0, 1, phi], [0, -1, phi], [0, 1, -phi], [0, -1, -phi],
    [1, phi, 0], [-1, phi, 0], [1, -phi, 0], [-1, -phi, 0],
    [phi, 0, 1], [-phi, 0, 1], [phi, 0, -1], [-phi, 0, -1]
];

// 2. Find all edges of the Icosahedron (Distance ~2.0)
iso_edges = [
    for (i = [0 : len(iso_verts)-2]) 
        for (j = [i+1 : len(iso_verts)-1])
            if ( abs(norm(iso_verts[i] - iso_verts[j]) - 2.0) < 0.1 ) 
                [i, j]
];

// 3. Generate the 60 vertices of the Truncated Icosahedron (Soccer Ball)
soccer_verts = [
    for (k = [0 : len(iso_edges)*2 - 1])
        normalize(
            iso_verts[iso_edges[floor(k/2)][0]] + 
            (iso_verts[iso_edges[floor(k/2)][1]] - iso_verts[iso_edges[floor(k/2)][0]]) * ((k % 2 == 0) ? 1/3 : 2/3)
        )
];

// 4. Pre-calculate neighbors for every vertex to generate fillets
neighbors_list = [
    for (i = [0 : len(soccer_verts)-1]) [
        for (j = [0 : len(soccer_verts)-1])
            if (i != j && norm(soccer_verts[i] - soccer_verts[j]) < 0.5) j
    ]
];

// --- RENDER ---

if (fast_render) {
    // Direct output (Fast!)
    draw_structure();
} else {
    // Intersect with sphere (Slow, high quality)
    intersection() {
        difference() {
            sphere(r = R_out, $fn=80);
            sphere(r = R_in, $fn=80);
        }
        draw_structure();
    }
}

module draw_structure() {
    union() {
        // A. Draw Straight Bars
        for (i = [0 : len(soccer_verts)-1]) {
            neighbors = neighbors_list[i];
            for (n_idx = neighbors) {
                if (i < n_idx) {
                    draw_bar(soccer_verts[i], soccer_verts[n_idx]);
                }
            }
        }
        
        // B. Draw Fillets (Curved Webbing at Nodes)
        for (i = [0 : len(soccer_verts)-1]) {
            draw_node_fillet(i, neighbors_list[i]);
        }
    }
}

// --- MODULES ---

module draw_bar(p1_unit, p2_unit) {
    draw_segment(p1_unit, p2_unit, bar_width);
}

module draw_node_fillet(center_idx, neighbors) {
    center_pt = soccer_verts[center_idx];
    
    for (i = [0 : len(neighbors)-1]) {
        n1_idx = neighbors[i];
        n2_idx = neighbors[(i+1) % len(neighbors)];
        
        p1 = soccer_verts[n1_idx];
        p2 = soccer_verts[n2_idx];
        
        dist1 = norm(p1 - center_pt) * R_struct_in; 
        dist2 = norm(p2 - center_pt) * R_struct_in;
        
        t1 = fillet_radius / dist1;
        t2 = fillet_radius / dist2;
        
        u_start = normalize(center_pt + (p1 - center_pt) * t1);
        u_end   = normalize(center_pt + (p2 - center_pt) * t2);
        
        // Reduced steps for performance (2 steps is usually enough for small fillets)
        steps = 4; 
        for (k = [0 : steps-1]) {
            t_a = k / steps;
            t_b = (k + 1) / steps;
            
            u_a_raw = (1-t_a)*(1-t_a)*u_start + 2*(1-t_a)*t_a*center_pt + t_a*t_a*u_end;
            u_a = normalize(u_a_raw);
            
            u_b_raw = (1-t_b)*(1-t_b)*u_start + 2*(1-t_b)*t_b*center_pt + t_b*t_b*u_end;
            u_b = normalize(u_b_raw);
            
            hull() {
                draw_point_column(center_pt, bar_width);
                draw_point_column(u_a, bar_width);
                draw_point_column(u_b, bar_width);
            }
        }
    }
}

module draw_segment(u1, u2, width) {
    p1_in  = u1 * R_struct_in;
    p1_out = u1 * R_struct_out;
    p2_in  = u2 * R_struct_in;
    p2_out = u2 * R_struct_out;
    
    hull() {
        // Low poly spheres for speed ($fn=8)
        translate(p1_in)  sphere(d = width, $fn=8);
        translate(p1_out) sphere(d = width, $fn=8);
        translate(p2_in)  sphere(d = width, $fn=8);
        translate(p2_out) sphere(d = width, $fn=8);
    }
}

module draw_point_column(u, width) {
    p_in  = u * R_struct_in;
    p_out = u * R_struct_out;
    
    hull() {
        // Low poly spheres for speed ($fn=8)
        translate(p_in)  sphere(d = width, $fn=16);
        translate(p_out) sphere(d = width, $fn=16);
    }
}

// --- MATH ---

function normalize(v) = v / norm(v);