/* This EAGLE "User Language Program" creates centroid files in a format that
 * is suitable for VisualPlace. If the board has components on both sides, two
 * centroid files are generated. The output files contain all fields required
 * by VisualPlace, so a separate BOM does not need to be generated.
 *
 * Version 1.2, November 2022
 * Thiadmer Riemersma, CompuPhase
 */

#usage "Creates centroid files in a format that is suitable for VisualPlace. "
       "Centroid files contain the positions of all SMD components.\n\n"
       "The generated centroid file contain all information that VisualPlace "
       "needs. A separate BOM (Bill Of Materials) is therefore not required.\n\n"
       "If the board has components on both sides, two centroid files are generated.\n\n"
       "Author: Thiadmer Riemersma, CompuPhase"

void printheader() {
    printf("# Created by VisualPlace at %s\n", t2string(time()));
    printf("# Report origin: (0.0, 0.0) = lower left corner of PCB\n");
    printf("# Units: inch\n");
    printf("# Only surface mount components are included\n");
    printf("\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\",\"%s\"\n\n",
           "Designator", "Value", "X", "Y", "Rotation", "Layer", "Type", "Package");
}

int smd(UL_PACKAGE P) {
    int smd = 0;
    P.contacts(C) {
        if (C.smd)
            smd = C.smd.layer;
    }
    return smd;
}

int throughhole(UL_PACKAGE P) {
    int smd = 0;
    int count = 0;
    P.contacts(C) {
        count += 1;
        if (C.smd)
            smd += 1;
    }
    if (smd == 0 && count > 0)
        return 1;       /* assume through-hole components are always on the top layer */
    return 0;           /* there are SMD pads, so assume it is an SMD component */
}

if (board) board(B) {
    string Filename, BaseFilename;
    int numTop, numBottom;

    /* ask for directory */
    BaseFilename = dlgDirectory("Select output directory", "") + '/' + filename(B.name);

    /* first check whether this board has components on both sides */
    numTop = 0;
    numBottom = 0;
    B.elements(E) {
        if (smd(E.package) == 1 || throughhole(E.package) == 1)
            numTop += 1;
        else if (smd(E.package) == 16)
            numBottom += 1;
    }

    /* if there are items on the top side, create a file for it */
    if (numTop > 0) {
        if (numBottom > 0)
            Filename = filesetext(BaseFilename, "-top.csv");
        else
            Filename = filesetext(BaseFilename, "-centroid.csv");
        output(Filename) {
            printheader();
            B.elements(E) {
                if (smd(E.package) == 1 || throughhole(E.package) == 1) {
                    string type;
                    if (smd(E.package) == 1)
                        type = "SMD";
                    else
                        type = "T/H";
                    printf("\"%s\",\"%s\",\"%6.4f\",\"%6.4f\",\"%.0f\",\"Top\",\"%s\",\"%s\"\n",
                           E.name, E.value, u2inch(E.x), u2inch(E.y),
                           E.angle, type, E.package.name);
                }
            }
        }
    }

    /* then do the same for the components at the bottom side, but flip the
     * horizontal axis (so that the positions are always relative to the lower
     * left corner)
     */
    if (numBottom > 0) {
        if (numTop > 0)
            Filename = filesetext(BaseFilename, "-bottom.csv");
        else
            Filename = filesetext(BaseFilename, "-centroid.csv");
        output(Filename) {
            printheader();
            real width = u2inch(B.area.x2 - B.area.x1);
            B.elements(E) {
                if (smd(E.package) == 16) {
                    string type = "SMD";
                    printf("\"%s\",\"%s\",\"%6.4f\",\"%6.4f\",\"%.0f\",\"Bottom\",\"%s\",\"%s\"\n",
                           E.name, E.value, width - u2inch(E.x), u2inch(E.y),
                           E.angle, type, E.package.name);
                }
            }
        }
    }
}
