panelThickness = 2;
panelHp=12;
holeCount=4;
holeWidth = 10.16; // If you want wider holes for easier mounting. Otherwise set to any number lower than mountHoleDiameter. Can be passed in as parameter to eurorackPanel()
holeOffset = 0.5;
jackHoles = true;
deepJackHoles = true;
jackHoleDiameter = 3.85; // If you want wider jack holes to compensate for your printer's precision. Or make it 3.4mm and use a 3.5mm drill bit to get proper hole sizes
jackHoleRows = 10;
jackHoleColumns = 5;
jackHoleDepth = 10;

threeUHeight = 133.35; // overall 3u height
panelOuterHeight =128.5;
panelInnerHeight = 110; // rail clearance = ~11.675mm, top and bottom
railHeight = (threeUHeight-panelOuterHeight)/2;
mountSurfaceHeight = (panelOuterHeight-panelInnerHeight-railHeight*2)/2;
panelInnerOffset = (panelOuterHeight-panelInnerHeight)/2; 

echo("railHeight: ", railHeight);
echo("mountSurfaceHeight",mountSurfaceHeight);

hp=5.08;
mountHoleDiameter = 3.2;
mountHoleRad =mountHoleDiameter/2;
hwCubeWidth = holeWidth-mountHoleDiameter;

offsetToMountHoleCenterY=mountSurfaceHeight/2;
offsetToMountHoleCenterX = hp - holeOffset; // 1 hp from side to center of hole, with a precision give to the side

echo("offsetToMountHoleCenterY: ", offsetToMountHoleCenterY);
echo("offsetToMountHoleCenterY: ", offsetToMountHoleCenterX);

module eurorackPanel(panelHp, jackHoles,  mountHoles=2, hw = holeWidth, ignoreMountHoles=false)
{
    // mountHoles ought to be even. Odd values are -=1
    difference()
    {
        union()
        {
            cube([hp*panelHp,panelOuterHeight,panelThickness]);
            
            if (deepJackHoles) {
                translate([0,panelInnerOffset,-jackHoleDepth])
                {
                    cube([hp*panelHp,panelInnerHeight,jackHoleDepth]);
                }
            }
        }
        
        if(!ignoreMountHoles)
        {
            eurorackMountHoles(panelHp, mountHoles, holeWidth);
        }
        
        if (jackHoles)
        {
            jackStorageHoles();
        }
    }
}

module jackStorageHoles()
{
    verticalJackHoleSpacing = (panelInnerHeight - jackHoleRows * jackHoleDiameter) / (jackHoleRows);
    horizontalJackHoleSpacing = (hp*panelHp - jackHoleColumns * jackHoleDiameter) / (jackHoleColumns + 1);
    echo("verticalJackHoleSpacing: ", verticalJackHoleSpacing);
    echo("horizontalJackHoleSpacing: ", horizontalJackHoleSpacing);
    for(verticalOffset = [panelInnerOffset + verticalJackHoleSpacing/2 + jackHoleDiameter/2 : verticalJackHoleSpacing + jackHoleDiameter : panelInnerHeight + jackHoleDiameter])
    {
        for(horizontalOffset = [horizontalJackHoleSpacing + jackHoleDiameter / 2 : jackHoleDiameter + horizontalJackHoleSpacing : hp*panelHp - horizontalJackHoleSpacing])
        {
            jackStorageHole(horizontalOffset, verticalOffset, jackHoleDiameter);
        }
    }
}

module jackStorageHole(horizontalOffset, verticalOffset, diameter)
{
    translate([horizontalOffset,verticalOffset,panelThickness - 4 - jackHoleDepth])
    {
            cylinder(r=diameter/2, h=jackHoleDepth+5, $fn=20);
    }
}

module eurorackMountHoles(php, holes, hw)
{
    holes = holes-holes%2;// mountHoles ought to be even for the sake of code complexity. Odd values are -=1
    eurorackMountHolesTopRow(php, hw, holes/2);
    eurorackMountHolesBottomRow(php, hw, holes/2);
}

module eurorackMountHolesTopRow(php, hw, holes)
{
    
    //topleft
    translate([offsetToMountHoleCenterX,panelOuterHeight-offsetToMountHoleCenterY,0])
    {
        eurorackMountHole(hw);
    }
    if(holes>1)
    {
        translate([(hp*php)-hwCubeWidth-hp,panelOuterHeight-offsetToMountHoleCenterY,0])
    {
        eurorackMountHole(hw);
    }
    }
    if(holes>2)
    {
        holeDivs = php*hp/(holes-1);
        for (i =[1:holes-2])
        {
            translate([holeDivs*i,panelOuterHeight-offsetToMountHoleCenterY,0]){
                eurorackMountHole(hw);
            }
        }
    }
}

module eurorackMountHolesBottomRow(php, hw, holes)
{
    
    //bottomRight
    translate([(hp*php)-hwCubeWidth-hp,offsetToMountHoleCenterY,0])
    {
        eurorackMountHole(hw);
    }
    if(holes>1)
    {
        translate([offsetToMountHoleCenterX,offsetToMountHoleCenterY,0])
    {
        eurorackMountHole(hw);
    }
    }
    if(holes>2)
    {
        holeDivs = php*hp/(holes-1);
        for (i =[1:holes-2])
        {
            translate([holeDivs*i,offsetToMountHoleCenterY,0]){
                eurorackMountHole(hw);
            }
        }
    }
}

module eurorackMountHole(hw)
{
    
    mountHoleDepth = panelThickness+2; // because diffs need to be larger than the object they are being diffed from for ideal BSP operations
    
    if(hwCubeWidth<0)
    {
        hwCubeWidth=0;
    }
    translate([0,0,-1]){
    union()
    {
        cylinder(r=mountHoleRad, h=mountHoleDepth, $fn=20);
        translate([0,-mountHoleRad,0]){
        cube([hwCubeWidth, mountHoleDiameter, mountHoleDepth]);
        }
        translate([hwCubeWidth,0,0]){
            cylinder(r=mountHoleRad, h=mountHoleDepth, $fn=20);
            }
    }
}
}

//Samples
//eurorackPanel(4, 2,holeWidth);
eurorackPanel(panelHp, jackHoles, holeCount, holeWidth);
//eurorackPanel(60, 8,holeWidth);