def place_bot_origin(x, y):
    # Solder mask fiducial
    fiducial = gerberex.read(ELEMENTS_DIR + "origin_1.12mm_dia_circle_soldermask.gbr")
    fiducial.to_metric()
    fiducial.offset(x, y)
    soldermask_bot_layer_context.merge(fiducial)

    # Copper cross
    fiducial = gerberex.read(ELEMENTS_DIR + "origin_cross.gbr")
    fiducial.to_metric()
    fiducial.offset(x, y)
    copper_layer_bot_context.merge(fiducial)
def place_bot_fiducial(x, y):
    # Solder mask fiducial
    fiducial = gerberex.read(ELEMENTS_DIR + "fiducial_2.30mm_dia_circle.gbr")
    fiducial.to_metric()
    fiducial.offset(x, y)
    soldermask_bot_layer_context.merge(fiducial)

    # Copper fiducial
    fiducial = gerberex.read(ELEMENTS_DIR + "fiducial_1.20mm_dia_circle.gbr")
    fiducial.to_metric()
    fiducial.offset(x, y)
    copper_layer_bot_context.merge(fiducial)
def main():
    start_time = time.time()

    setup()

    panel_width = frame_width * 2 + cutout_width * 3 + 57.15 * 2
    panel_height = frame_width * 2 + cutout_width * 3 + 57.15 + 111.76

    # Top layer
    cream_layer = gerberex.read(INPUT_DIR + "axiom_beta_mixed_panel.topcream.ger")
    cream_layer.to_metric()
    stencil_context.merge(cream_layer)

    engrave_layer = gerberex.read(INPUT_DIR + "axiom_beta_mixed_panel.topengrave.ger")
    engrave_layer.to_metric()
    engrave_context.merge(engrave_layer)

    copper_layer = gerberex.read(INPUT_DIR + "axiom_beta_mixed_panel.toplayer.ger")
    copper_layer.to_metric()
    copper_context.merge(copper_layer)

    soldermask_layer = gerberex.read(INPUT_DIR + "axiom_beta_mixed_panel.topsoldermask.ger")
    soldermask_layer.to_metric()
    soldermask_context.merge(soldermask_layer)



    # Bottom layer
    offset = 210
    cream_layer = gerberex.read(INPUT_DIR + "axiom_beta_mixed_panel.bottomcream-mirrored.ger")
    cream_layer.to_metric()
    cream_layer.offset(offset)
    stencil_context.merge(cream_layer)

    engrave_layer = gerberex.read(INPUT_DIR + "axiom_beta_mixed_panel.bottomengrave-mirrored.ger")
    engrave_layer.to_metric()
    engrave_layer.offset(offset)
    engrave_context.merge(engrave_layer)

    copper_layer = gerberex.read(INPUT_DIR + "axiom_beta_mixed_panel.bottomlayer-mirrored.ger")
    copper_layer.to_metric()
    copper_layer.offset(offset)
    copper_context.merge(copper_layer)

    soldermask_layer = gerberex.read(INPUT_DIR + "axiom_beta_mixed_panel.bottomsoldermask-mirrored.ger")
    soldermask_layer.to_metric()
    soldermask_layer.offset(offset)
    soldermask_context.merge(soldermask_layer)


    stencil_context.dump(OUTPUT_DIR + "axiom_beta_mixed_panel_stencil.cut.ger")
    engrave_context.dump(OUTPUT_DIR + "axiom_beta_mixed_panel_stencil.engrave.ger")
    copper_context.dump(OUTPUT_DIR + "axiom_beta_mixed_panel_stencil.copper.ger")
    soldermask_context.dump(OUTPUT_DIR + "axiom_beta_mixed_panel_stencil.soldermask.ger")

    end_time = time.time()
    elapsed_time = end_time - start_time
    print("\r\nElapsed time: %.2f" % elapsed_time, "seconds")
Beispiel #4
0
 def _test_to_metric(self):
     outfile = os.path.join(self.OUTDIR, self.OUTPREFIX + 'to_metric.gtl')
     dxf = gerberex.read(self.INCH_FILE)
     dxf.to_metric()
     dxf.format = (3, 5)
     dxf.write(outfile)
     self._checkResult(outfile)
 def test_to_inch(self):
     outfile = os.path.join(self.OUTDIR, self.OUTPREFIX + 'to_inch.txt')
     drill = gerberex.read(self.METRIC_FILE)
     drill.to_inch()
     drill.format = (2, 4)
     drill.write(outfile)
     self._checkResult(outfile)
 def test_to_metric(self):
     outfile = os.path.join(self.OUTDIR, self.OUTPREFIX + 'to_metric.txt')
     drill = gerberex.read(self.INCH_FILE)
     drill.to_metric()
     drill.format = (3, 3)
     drill.write(outfile)
     self._checkResult(outfile)
Beispiel #7
0
def place_subpanel_label(x, y):
    # silk screen label
    label = gerberex.read(ELEMENTS_DIR + "subpanel_label_eagle7.ger")
    label.to_metric()
    label.rotate(90)
    label.offset(x, y)
    silkscreen_top_layer_context.merge(label)
 def test_save_fill_simple(self):
     outfile = os.path.join(self.OUTDIR, self.OUTPREFIX + 'save_fill_simple.gtl')
     dxf = gerberex.read(self.METRIC_FILE)
     dxf.draw_mode = dxf.DM_FILL
     dxf.fill_mode = dxf.FM_SIMPLE
     dxf.write(outfile)
     self._checkResult(outfile)
Beispiel #9
0
 def test_to_metric(self):
     outfile = os.path.join(self.OUTDIR, self.OUTPREFIX + 'to_metric.gtl')
     gerber = gerberex.read(self.INCH_FILE)
     gerber.to_metric()
     gerber.format = (3, 4)
     gerber.write(outfile)
     self._checkResult(outfile)
Beispiel #10
0
 def test_save_line(self):
     outfile = os.path.join(self.OUTDIR, self.OUTPREFIX + 'save_line.gtl')
     dxf = gerberex.read(self.METRIC_FILE)
     dxf.draw_mode = dxf.DM_LINE
     dxf.width = 0.2
     dxf.write(outfile)
     self._checkResult(outfile)
def add_pcb(pcb_name, x, y, rotate=False, generate_frame=True, merge_outline=True):
    pcb_file_path = INPUT_DIR + pcb_name + "/" + pcb_name
    board_outline_file_path = pcb_file_path + '.boardoutline.ger'
    board_outline = gerberex.read(board_outline_file_path)
    board_outline.to_metric()
    # if rotate:
    #    board_outline.rotate(90)

    # Reset the board offset, set the bottom-left point of the board to 0, 0
    board_offset_x = board_outline.bounds[0][0]
    board_offset_y = board_outline.bounds[1][0]

    board_width = board_outline.size[0]
    board_height = board_outline.size[1]
    board_pos_x = x + frame_width + cutout_width
    board_pos_y = y + frame_width + cutout_width
    if generate_frame:
        generate_pcb_frame(board_cutout_msp, board_pos_x, board_pos_y, board_width, board_height, cutout_width)

    board_pos_x -= board_offset_x
    board_pos_y -= board_offset_y

    remove_gerber_outline(board_outline)

    board_outline.offset(board_pos_x, board_pos_y)
    if merge_outline:
        board_outline_context.merge(board_outline)

    add_layer(copper_layer_top_context, pcb_file_path + ".toplayer.ger", board_pos_x, board_pos_y, rotate)
    add_layer(copper_layer_bot_context, pcb_file_path + ".bottomlayer.ger", board_pos_x, board_pos_y, rotate)

    pcb_info.append([pcb_name, x, y, board_width, board_height, board_offset_x, board_offset_y])
Beispiel #12
0
 def test_to_inch(self):
     outfile = os.path.join(self.OUTDIR, self.OUTPREFIX + 'to_inch.gtl')
     dxf = gerberex.read(self.METRIC_FILE)
     dxf.to_inch()
     dxf.format = (2, 5)
     dxf.write(outfile)
     self._checkResult(outfile)
Beispiel #13
0
 def test_save_mousebites(self):
     outfile = os.path.join(self.OUTDIR, self.OUTPREFIX + 'save_mousebites.gtl')
     dxf = gerberex.read(self.MOUSEBITES_FILE)
     dxf.draw_mode = dxf.DM_MOUSE_BITES
     dxf.width = 0.5
     dxf.pitch = 1
     dxf.write(outfile)
     self._checkResult(outfile)
def add_drill_layer(context, file_path, x, y, rotate):
    layer = gerberex.read(file_path, format=(2, 4))
    layer.to_metric()

    # if rotate:
    #    layer.rotate(90)

    layer.offset(x, y)
    context.merge(layer)
Beispiel #15
0
 def test_save_excellon(self):
     outfile = os.path.join(
         self.OUTDIR, self.OUTPREFIX + 'save_line.txt')
     dxf = gerberex.read(self.METRIC_FILE)
     dxf.draw_mode = dxf.DM_LINE
     dxf.format = (3,3)
     dxf.width = 0.2
     dxf.write(outfile, filetype=dxf.FT_EXCELLON)
     self._checkResult(outfile)
Beispiel #16
0
def add_layer(context, file_path, x, y, rotate):
    layer = gerberex.read(file_path)
    layer.to_metric()

    if rotate:
        layer.rotate(90)
    layer.offset(x, y)

    context.merge(layer)
Beispiel #17
0
 def test_save_excellon(self):
     outfile = os.path.join(
         self.OUTDIR, self.OUTPREFIX + 'save_mousebites.txt')
     dxf = gerberex.read(self.MOUSEBITES_FILE)
     dxf.draw_mode = dxf.DM_MOUSE_BITES
     dxf.format = (3,3)
     dxf.width = 0.5
     dxf.pitch = 1
     dxf.write(outfile, filetype=dxf.FT_EXCELLON)
     self._checkResult(outfile)
Beispiel #18
0
 def test_complex_fill_flip(self):
     outfile = os.path.join(
         self.OUTDIR, self.OUTPREFIX + 'complex_fill_flip.gtl')
     ctx = gerberex.GerberComposition()
     base = gerberex.rectangle(width=100, height=100, left=0, bottom=0, units='metric')
     base.draw_mode = base.DM_FILL
     ctx.merge(base)
     dxf = gerberex.read(self.COMPLEX_FILE)
     dxf.negate_polarity()
     dxf.draw_mode = dxf.DM_FILL
     ctx.merge(dxf)
     ctx.dump(outfile)
     self._checkResult(outfile)
def setup():
    if not os.path.exists(OUTPUT_DIR):
        os.makedirs(OUTPUT_DIR)
    if not os.path.exists(TEMP_DIR):
        os.makedirs(TEMP_DIR)

    background_top = INPUT_DIR + "axiom_beta_mixed_panel.top_rotated.png"
    background_bottom = INPUT_DIR + "axiom_beta_mixed_panel.bottom_rotated.png"

    board_x = 0
    board_y = 0

    board_outline = gerberex.read('output_stage1/axiom_beta_mixed_panel.boardoutline.ger')

    board_width = board_outline.size[1]
    board_height = board_outline.size[0]

    image_width, image_height = get_image_size(background_top)

    global drawing_top, drawing_bottom, factor, output_image_height

    output_image_width = image_width / 2
    output_image_height = image_height / 2

    factor = (output_image_width / board_width + output_image_height / board_height) / 2

    # Fixed size for PCB image
    drawing_top = draw.Drawing(output_image_width, output_image_height)
    drawing_bottom = draw.Drawing(output_image_width, output_image_height)

    # Draw background
    drawing_top.append(draw.Image(board_x * factor, board_y * factor,
                                  output_image_width, output_image_height, background_top))

    drawing_top.append(draw.Rectangle(0, 0, output_image_width, output_image_height, fill="black", opacity=0.5))

    drawing_bottom.append(draw.Image(board_x * factor, board_y * factor,
                                     output_image_width, output_image_height, background_bottom))

    drawing_bottom.append(draw.Rectangle(0, 0, output_image_width, output_image_height, fill="black", opacity=0.5))
Beispiel #20
0
def add_pcb(pcb_name, x, y, rotate=False):
    pcb_file_path = INPUT_DIR + "/" + pcb_name
    board_outline_file_path = pcb_file_path + '.boardoutline.ger'
    board_outline = gerberex.read(board_outline_file_path)
    board_outline.to_metric()
    if rotate:
        board_outline.rotate(90)

    # Reset the board offset, set the bottom-left point of the board to 0, 0
    board_offset_x = board_outline.bounds[0][0]
    board_offset_y = board_outline.bounds[1][0]

    board_width = board_outline.size[0]
    board_height = board_outline.size[1]
    board_pos_x = x + frame_width + cutout_width
    board_pos_y = y + frame_width + cutout_width
    generate_pcb_frame(board_cutout_msp, board_pos_x - frame_width, board_pos_y - frame_width,
                       board_width + frame_width * 2, board_height + frame_width * 2, cutout_width)

    board_pos_x -= board_offset_x
    board_pos_y -= board_offset_y

    board_outline.offset(board_pos_x, board_pos_y)
    board_outline_context.merge(board_outline)

    add_layer(copper_layer_top_context, pcb_file_path + ".toplayer.ger", board_pos_x, board_pos_y, rotate)
    add_layer(soldermask_top_layer_context, pcb_file_path + ".topsoldermask.ger", board_pos_x, board_pos_y, rotate)
    add_layer(silkscreen_top_layer_context, pcb_file_path + ".topsilkscreen.ger", board_pos_x, board_pos_y, rotate)
    add_layer(copper_layer_bot_context, pcb_file_path + ".bottomlayer.ger", board_pos_x, board_pos_y, rotate)
    add_layer(soldermask_bot_layer_context, pcb_file_path + ".bottomsoldermask.ger", board_pos_x, board_pos_y, rotate)
    add_layer(silkscreen_bot_layer_context, pcb_file_path + ".bottomsilkscreen.ger", board_pos_x, board_pos_y, rotate)
    add_layer(cream_top_layer_context, pcb_file_path + ".topcream.ger", board_pos_x, board_pos_y, rotate)
    add_layer(cream_bot_layer_context, pcb_file_path + ".bottomcream.ger", board_pos_x, board_pos_y, rotate)
    add_layer(internalplane1_layer_context, pcb_file_path + ".internalplane1.ger", board_pos_x, board_pos_y, rotate)
    add_layer(internalplane2_layer_context, pcb_file_path + ".internalplane2.ger", board_pos_x, board_pos_y, rotate)

    add_layer(drills_context, pcb_file_path + ".drills.xln", board_pos_x, board_pos_y, rotate)

    pcb_info.append([pcb_name, x, y, board_width, board_height, board_offset_x, board_offset_y])
def main():
    start_time = time.time()

    setup()

    panel_width = frame_width * 2 + cutout_width * 3 + 57.15 * 2
    panel_height = frame_width * 2 + cutout_width * 3 + 57.15 + 111.76

    # Commented out to disable outer frame for mixed panel (stage 2)
    generate_outer_frame(board_cutout_msp, panel_width, panel_height)

    add_pcb("axiom_beta_sensor_cmv12000_tht_v0.16_r1.8c_impedance", 0, 0)
    add_pcb("axiom_beta_interface_dummy_v0.13_r1.6_impedance", 57.15 + cutout_width, 0)
    add_pcb("axiom_beta_main_board_v0.38_r1.2_impedance", 0, 57.15 + cutout_width)
    add_pcb("axiom_beta_power_board_v0.38_r1.2_impedance", 57.15 + cutout_width, 57.15 + cutout_width)

    # impedance test strip
    add_pcb("test_strip_v0.1_r1.2_impedance", 0.1, 0.1, generate_frame=False, merge_outline=False)

    area = [0, 0, panel_width, panel_height]
    #generate_pcb_bridges(board_cutout_msp, area, cutout_width, 4, 6)

    board_cutout_doc.saveas(TEMP_DIR + 'board_outline.dxf')
    dxf_file = gerberex.read(TEMP_DIR + 'board_outline.dxf')
    board_outline_context.merge(dxf_file)
    board_outline_context.dump(OUTPUT_DIR + "axiom_beta_mixed_panel.boardoutline.ger")

    copper_layer_top_context.dump(OUTPUT_DIR + "axiom_beta_mixed_panel.toplayer.ger")
    copper_layer_bot_context.dump(OUTPUT_DIR + "axiom_beta_mixed_panel.bottomlayer.ger")


    print(tabulate(pcb_info, headers=['Name', 'X', 'Y', 'Width', 'Height', 'Offset X', 'Offset Y'],
                   tablefmt='orgtbl'))

    end_time = time.time()
    elapsed_time = end_time - start_time
    print("\r\nElapsed time: %.2f" % elapsed_time, "seconds")
Beispiel #22
0
#!/usr/bin/env python
import gerberex, os
from gerberex import DxfFile, GerberComposition, DrillComposition

exts = ['GTL', 'GTO', 'GTS', 'GBL', 'GBO', 'GBS']
boards = [
    ('../../pcb/type2/CAMOutputs/sonopi-dac-type2.', 0, 0, 0),
]
outline = gerberex.read('../../pcb/type2/CAMOutputs/sonopi-dac-type2.GML')
outputs = 'outputs/pcb'

if not os.path.isdir('outputs'):
    os.mkdir('outputs')

for ext in exts:
    print('merging %s: ' % ext, end='', flush=True)
    if ext == 'TXT':
        ctx = DrillComposition()
    else:
        ctx = GerberComposition()
    for board in boards:
        file = gerberex.read(board[0] + ext)
        file.to_metric()
        if ext == 'GTO' or ext == 'GBO':
            for adef in file.aperture_defs:
                if adef.shape == 'C' and adef.modifiers[0][0] < 0.12:
                    adef.modifiers[0] = (0.12, )
                elif adef.shape == 'R' and adef.modifiers[0][1] < 0.05:
                    adef.modifiers[0] = (adef.modifiers[0][0], 0.05)
        file.rotate(board[3])
        file.offset(board[1], board[2])
def place_panel_label(x, y):
    # silk screen label
    label = gerberex.read(ELEMENTS_DIR + "panel_label.gbr")
    label.to_metric()
    label.offset(x, y)
    silkscreen_top_layer_context.merge(label)
Beispiel #24
0
    ('../small/CAMOutputs/rcstick-f-small.', 55.4, 45, 180),
    ('../small/CAMOutputs/rcstick-f-small.', 75.4, 45, 180),
    ('../small/CAMOutputs/rcstick-f-small.', 95.4, 45, 180),
]
outline = 'outline.dxf'
mousebites = 'mousebites.dxf'
outputs = 'outputs/rcstick-panelized'

for ext in exts:
    print('merging %s: ' % ext ,end='', flush=True)
    if ext == 'TXT':
        ctx = DrillComposition()
    else:
        ctx = GerberComposition()
    for board in boards:
        file = gerberex.read(board[0] + ext)
        file.to_metric()
        file.rotate(board[3])
        file.offset(board[1], board[2])
        ctx.merge(file)
        print('.', end='', flush=True)
    if ext == 'TXT':
        file = gerberex.read(mousebites)
        file.draw_mode = DxfFile.DM_MOUSE_BITES
        file.width = 0.5
        file.format = (3, 3)
        ctx.merge(file)
    else:
        file = gerberex.read(outline)
        ctx.merge(file)
    ctx.dump(outputs + '.' + ext)
 def test_rotate(self):
     outfile = os.path.join(self.OUTDIR, self.OUTPREFIX + 'rotate.txt')
     drill = gerberex.read(self.METRIC_FILE)
     drill.rotate(20, (10, 10))
     drill.write(outfile)
     self._checkResult(outfile)
 def test_offset(self):
     outfile = os.path.join(self.OUTDIR, self.OUTPREFIX + 'offset.txt')
     drill = gerberex.read(self.METRIC_FILE)
     drill.offset(11, 5)
     drill.write(outfile)
     self._checkResult(outfile)
def main():
    start_time = time.time()

    setup()

    panel_width = frame_width * 2 + cutout_width * 3 + 57.15 * 2
    panel_height = frame_width * 2 + cutout_width * 3 + 57.15 + 111.76

    # Commented out to disable outer frame for mixed panel (stage 2)
    generate_outer_frame(board_cutout_msp, panel_width, panel_height)

    add_pcb("axiom_beta_sensor_cmv12000_tht_v0.16_r1.8c", 0, 0)
    add_pcb("axiom_beta_interface_dummy_v0.13_r1.6", 57.15 + cutout_width, 0)
    add_pcb("axiom_beta_main_board_v0.38_r1.2", 0, 57.15 + cutout_width)
    add_pcb("axiom_beta_power_board_v0.38_r1.2", 57.15 + cutout_width, 57.15 + cutout_width)

    # impedance test strip
    add_pcb("test_strip_v0.1_r1.2", 0.1, 0.1, generate_frame=False, merge_outline=False)

    area = [0, 0, panel_width, panel_height]
    generate_pcb_bridges(board_cutout_msp, area, cutout_width, 4, 6)

    # fiducials
    place_top_fiducial(5, 2.5)
    place_top_fiducial(5, panel_height - 2.5)
    place_top_fiducial(panel_width - 5, panel_height - 2.5)
    place_bot_fiducial(5, 2.5)
    place_bot_fiducial(5, panel_height - 2.5)
    place_bot_fiducial(panel_width - 5, panel_height - 2.5)

    # origin
    place_top_origin(panel_width - 5, 2.5)
    place_bot_origin(panel_width - 5, 2.5)

    # labels
    place_subpanel_label(3.5, 8)

    board_cutout_doc.saveas(TEMP_DIR + 'board_outline.dxf')
    dxf_file = gerberex.read(TEMP_DIR + 'board_outline.dxf')
    board_outline_context.merge(dxf_file)
    board_outline_context.dump(OUTPUT_DIR + "axiom_beta_mixed_panel.boardoutline.ger")

    board_info_doc.saveas(TEMP_DIR + 'board_info.dxf')
    dxf_file = gerberex.read(TEMP_DIR + 'board_info.dxf')
    dxf_file.width = 0.2
    silkscreen_top_layer_context.merge(dxf_file)

    copper_layer_top_context.dump(OUTPUT_DIR + "axiom_beta_mixed_panel.toplayer.ger")
    soldermask_top_layer_context.dump(OUTPUT_DIR + "axiom_beta_mixed_panel.topsoldermask.ger")
    silkscreen_top_layer_context.dump(OUTPUT_DIR + "axiom_beta_mixed_panel.topsilkscreen.ger")
    copper_layer_bot_context.dump(OUTPUT_DIR + "axiom_beta_mixed_panel.bottomlayer.ger")
    soldermask_bot_layer_context.dump(OUTPUT_DIR + "axiom_beta_mixed_panel.bottomsoldermask.ger")
    silkscreen_bot_layer_context.dump(OUTPUT_DIR + "axiom_beta_mixed_panel.bottomsilkscreen.ger")
    cream_top_layer_context.dump(OUTPUT_DIR + "axiom_beta_mixed_panel.topcream.ger")
    cream_bot_layer_context.dump(OUTPUT_DIR + "axiom_beta_mixed_panel.bottomcream.ger")
    internalplane1_layer_context.dump(OUTPUT_DIR + "axiom_beta_mixed_panel.internalplane1.ger")
    internalplane2_layer_context.dump(OUTPUT_DIR + "axiom_beta_mixed_panel.internalplane2.ger")

    drills_context.dump(OUTPUT_DIR + "axiom_beta_mixed_panel.drills.xln")

    print(tabulate(pcb_info, headers=['Name', 'X', 'Y', 'Width', 'Height', 'Offset X', 'Offset Y'],
                   tablefmt='orgtbl'))

    end_time = time.time()
    elapsed_time = end_time - start_time
    print("\r\nElapsed time: %.2f" % elapsed_time, "seconds")
Beispiel #28
0
    pass


def putstr(text):
    sys.stdout.write(text)
    sys.stdout.flush()


for ext in exts:
    putstr('merging %s: ' % ext)
    if ext == 'TXT':
        ctx = DrillComposition()
    else:
        ctx = GerberComposition()
    for path, x_offset, y_offset, angle in boards:
        file = gerberex.read(path + ext)
        file.to_metric()
        file.rotate(angle)
        file.offset(x_offset, y_offset)
        ctx.merge(file)
        putstr('.')
    if ext == 'TXT':
        file = gerberex.read(mousebites)
        file.draw_mode = DxfFile.DM_MOUSE_BITES
        file.to_metric()
        file.width = 0.5
        file.format = (3, 3)
        ctx.merge(file)
    else:
        file = gerberex.read(outline)
        ctx.merge(file)
 def test_save(self):
     outfile = os.path.join(self.OUTDIR, self.OUTPREFIX + 'save.txt')
     drill = gerberex.read(self.METRIC_FILE)
     drill.write(outfile)
     self._checkResult(outfile)
Beispiel #30
0
#!/usr/bin/env python
import gerberex, os
from gerberex import DxfFile, GerberComposition, DrillComposition

exts = ['GTL', 'GTO', 'GTS', 'GBL', 'GBO', 'GBS']
boards = [
    ('../../pcb/tiny/CAMOutputs/rcstick-f-tiny.', 0, 0, 0),
]
outline = gerberex.read('../../pcb/tiny/CAMOutputs/rcstick-f-tiny.GML')
outputs = 'outputs/pcb'

if not os.path.isdir('outputs'):
    os.mkdir('outputs')

for ext in exts:
    print('merging %s: ' % ext, end='', flush=True)
    if ext == 'TXT':
        ctx = DrillComposition()
    else:
        ctx = GerberComposition()
    for board in boards:
        file = gerberex.read(board[0] + ext)
        file.to_metric()
        if ext == 'GTO' or ext == 'GBO':
            for adef in file.aperture_defs:
                if adef.shape == 'C' and adef.modifiers[0][0] < 0.12:
                    adef.modifiers[0] = (0.12, )
                elif adef.shape == 'R' and adef.modifiers[0][1] < 0.05:
                    adef.modifiers[0] = (adef.modifiers[0][0], 0.05)
        file.rotate(board[3])
        file.offset(board[1], board[2])