//
// colorpie.scad
// 2026-03-06 ChatGPT
// $Header$
//
// Preview:
//   openscad colorpie.scad
//
// STL export:
//   openscad -o output.stl wedge.scad
//

$fn = 90;

// ------------------------------------------------------------
// Global parameters
// ------------------------------------------------------------

slice_count      = 10;
slice_angle      = 360 / slice_count;     // 36 degrees
explode_gap      = 10;

// Footprint
inner_r          = 22;
outer_r          = 82;
thickness        = 6;

// Crown profile across the wedge width
crown_height     = 10;
table_frac       = 0.42;                  // fraction of width occupied by table
shoulder_frac    = 0.18;                  // fraction on each side used by bevel/shoulder
stations         = 13;                    // more stations = more facet slices across width


// Labeling
label_size       = 8;
label_depth      = 1.0;
label_font       = "Liberation Sans:style=Bold";

// Small geometry tolerance
eps              = 0.01;


// ------------------------------------------------------------
// Helper functions
// ------------------------------------------------------------

function deg(a) = a;
function lerp(a,b,t) = a + (b-a)*t;

// u is normalized from 0 at left cut face to 1 at right cut face
// This defines the outside-elevation style profile:
//
//   low edge -> bevel up -> flat table -> bevel down -> low edge
//
function crown_profile(u) =
    let(
        t0 = shoulder_frac,
        t1 = 0.5 - table_frac/2,
        t2 = 0.5 + table_frac/2,
        t3 = 1.0 - shoulder_frac
    )
    (u <= t0) ? lerp(0.00, 0.70, u / t0) :
    (u <= t1) ? lerp(0.70, 1.00, (u - t0) / max(eps, t1 - t0)) :
    (u <= t2) ? 1.00 :
    (u <= t3) ? lerp(1.00, 0.70, (u - t2) / max(eps, t3 - t2)) :
                lerp(0.70, 0.00, (u - t3) / max(eps, 1.0 - t3));

// Z value of top surface at station u
function top_z(u) = thickness + crown_height * crown_profile(u);

// Angle at station i
function station_angle(i) = slice_angle * i / (stations - 1);

// Polar point helpers
function p_inner(a,z) = [ inner_r * cos(a), inner_r * sin(a), z ];
function p_outer(a,z) = [ outer_r * cos(a), outer_r * sin(a), z ];

// Indices for point grid
// per station:
//   0 = bottom inner
//   1 = bottom outer
//   2 = top inner
//   3 = top outer
function idx_bi(i) = 4*i + 0;
function idx_bo(i) = 4*i + 1;
function idx_ti(i) = 4*i + 2;
function idx_to(i) = 4*i + 3;


// ------------------------------------------------------------
// Master wedge as a polyhedron
// Top shape traverses the wedge width
// ------------------------------------------------------------

module wedge_body() {

    pts = [
        for (i = [0:stations-1])
            let(
                a = station_angle(i),
                u = i / (stations - 1),
                zt = top_z(u)
            )
            each [
                p_inner(a, 0),   // bottom inner
                p_outer(a, 0),   // bottom outer
                p_inner(a, zt),  // top inner
                p_outer(a, zt)   // top outer
            ]
    ];

    faces = concat(

        // bottom, triangulated
        [
            for (i = [0:stations-2]) each [
                [ idx_bi(i),   idx_bi(i+1), idx_bo(i+1) ],
                [ idx_bi(i),   idx_bo(i+1), idx_bo(i)   ]
            ]
        ],

        // top, triangulated
        [
            for (i = [0:stations-2]) each [
                [ idx_ti(i),   idx_to(i),   idx_to(i+1) ],
                [ idx_ti(i),   idx_to(i+1), idx_ti(i+1) ]
            ]
        ],

        // inner radius face, triangulated
        [
            for (i = [0:stations-2]) each [
                [ idx_bi(i),   idx_ti(i),   idx_ti(i+1) ],
                [ idx_bi(i),   idx_ti(i+1), idx_bi(i+1) ]
            ]
        ],

        // outer radius face, triangulated
        [
            for (i = [0:stations-2]) each [
                [ idx_bo(i),   idx_bo(i+1), idx_to(i+1) ],
                [ idx_bo(i),   idx_to(i+1), idx_to(i)   ]
            ]
        ],

        // left cut face, triangulated
        [
            [ idx_bi(0), idx_bo(0), idx_to(0) ],
            [ idx_bi(0), idx_to(0), idx_ti(0) ]
        ],

        // right cut face, triangulated
        [
            [ idx_bi(stations-1), idx_ti(stations-1), idx_to(stations-1) ],
            [ idx_bi(stations-1), idx_to(stations-1), idx_bo(stations-1) ]
        ]
    );

    polyhedron(points = pts, faces = faces, convexity = 12);
}


// ------------------------------------------------------------
// raised bottom label so readable and not engulged/hidden
// ------------------------------------------------------------
module wedge_label_raise_OLD(label_txt="0") {
    mid_r = (inner_r + outer_r) / 2;
    mid_a = slice_angle / 2;

    x = mid_r * cos(mid_a);
    y = mid_r * sin(mid_a);

    translate([x, y, 0])
    rotate([0, 0, mid_a - 90])
    linear_extrude(height = 1.2)
        text(label_txt,
             size   = 18,
             font   = label_font,
             halign = "center",
             valign = "center");
}

// If the text is too close to the middle or too large, use this instead:


module wedge_label_raise_optional(label_txt="0") {
    mid_r = inner_r + (outer_r - inner_r) * 0.63;
    mid_a = slice_angle / 2;

    x = mid_r * cos(mid_a);
    y = mid_r * sin(mid_a);

    translate([x, y, 0])
    rotate([0, 0, mid_a - 90])
    linear_extrude(height = 10.2)
        text(label_txt,
             size   = 18,
             font   = label_font,
             halign = "center",
             valign = "center");
}

module wedge_label_raise(label_txt="0") {
    mid_r = inner_r + (outer_r - inner_r) * 0.63;
    mid_a = slice_angle / 2;

    x = mid_r * cos(mid_a);
    y = mid_r * sin(mid_a);

    translate([x, y, -3.2])
    rotate([0, 0, mid_a - 90])
    linear_extrude(height = 3)
        text(label_txt,
             size   = 16,
             font   = label_font,
             halign = "center",
             valign = "center");
}
// ------------------------------------------------------------
// One numbered wedge
// ------------------------------------------------------------

module wedge_unit(idx=0) {
    wedge_body();
    wedge_label_raise(str(idx));
    //wedge_label_raise_optional(str(idx));
}


// ------------------------------------------------------------
// Assemble 10 wedges around the center
// Each wedge is exploded outward along its centerline
// ------------------------------------------------------------

module wheel_10() {
    for (i = [0:slice_count-1]) {
        a = i * slice_angle;
        mid_a = a + slice_angle/2;

        dx = explode_gap * cos(mid_a);
        dy = explode_gap * sin(mid_a);

        translate([dx, dy, 0])
            rotate([0,0,a])
                wedge_unit(i);
    }
}


// ------------------------------------------------------------
// Top level
// ------------------------------------------------------------

wheel_10();
