From fba189c69534fe0e88851e35716fe0e5d07a5c98 Mon Sep 17 00:00:00 2001 From: jaseg Date: Sun, 9 Apr 2023 17:24:50 +0200 Subject: protogen web interface works --- gerbonara/cad/protoserve.py | 131 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 gerbonara/cad/protoserve.py (limited to 'gerbonara/cad/protoserve.py') diff --git a/gerbonara/cad/protoserve.py b/gerbonara/cad/protoserve.py new file mode 100644 index 0000000..0bf9dce --- /dev/null +++ b/gerbonara/cad/protoserve.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python + +import importlib.resources +from tempfile import TemporaryDirectory +from pathlib import Path + +from quart import Quart, request, Response + +from . import protoboard as pb +from . import protoserve_data +from ..utils import MM, Inch + + +def extract_importlib(package): + root = TemporaryDirectory() + + stack = [(importlib.resources.files(package), Path(root.name))] + while stack: + res, out = stack.pop() + + for item in res.iterdir(): + item_out = out / item.name + if item.is_file(): + item_out.write_bytes(item.read_bytes()) + else: + assert item.is_dir() + item_out.mkdir() + stack.push((item, item_out)) + + return root + +static_folder = extract_importlib(protoserve_data) +app = Quart(__name__, static_folder=static_folder.name) + +@app.route('/') +async def index(): + return await app.send_static_file('protoserve.html') + +def deserialize(obj, unit): + pitch_x = float(obj.get('pitch_x', 1.27)) + pitch_y = float(obj.get('pitch_y', 1.27)) + clearance = float(obj.get('clearance', 0.2)) + + match obj['type']: + case 'layout': + proportions = [float(child['layout_prop']) for child in obj['children']] + content = [deserialize(child, unit) for child in obj['children']] + return pb.PropLayout(content, obj['direction'], proportions) + + case 'placeholder': + return pb.EmptyProtoArea() + + case 'smd': + match obj['pad_shape']: + case 'rect': + pad = pb.SMDPad.rect(0, 0, pitch_x-clearance, pitch_y-clearance, paste=False, unit=unit) + case 'circle': + pad = pb.SMDPad.circle(0, 0, min(pitch_x, pitch_y)-clearance, paste=False, unit=unit) + return pb.PatternProtoArea(pitch_x, pitch_y, obj=pad, unit=unit) + + case 'tht': + hole_dia = float(obj['hole_dia']) + match obj['plating']: + case 'plated': + oneside, plated = False, True + case 'nonplated': + oneside, plated = False, False + case 'singleside': + oneside, plated = True, False + + match obj['pad_shape']: + case 'rect': + pad = pb.THTPad.rect(0, 0, hole_dia, pitch_x-clearance, pitch_y-clearance, paste=False, plated=plated, unit=unit) + case 'circle': + pad = pb.THTPad.circle(0, 0, hole_dia, min(pitch_x, pitch_y)-clearance, paste=False, plated=plated, unit=unit) + case 'obround': + pad = pb.THTPad.obround(0, 0, hole_dia, pitch_x-clearance, pitch_y-clearance, paste=False, plated=plated, unit=unit) + + if oneside: + pad.pad_bottom = None + + return pb.PatternProtoArea(pitch_x, pitch_y, obj=pad, unit=unit) + + case 'manhattan': + return pb.PatternProtoArea(pitch_x, pitch_y, obj=pb.ManhattanPads(pitch_x, pitch_y, clearance, unit=unit), unit=unit) + + case 'powered': + pitch = float(obj.get('pitch', 2.54)) + hole_dia = float(obj['hole_dia']) + via_drill = float(obj['via_hole_dia']) + trace_width = float(obj['trace_width']) + return pb.PatternProtoArea(pitch, pitch, pb.PoweredProto(pitch, hole_dia, clearance, via_size=via_drill, trace_width=trace_width, unit=unit), unit=unit) + + case 'flower': + pitch = float(obj.get('pitch', 2.54)) + hole_dia = float(obj['hole_dia']) + pattern_dia = float(obj['pattern_dia']) + return pb.PatternProtoArea(2*pitch, 2*pitch, pb.THTFlowerProto(pitch, hole_dia, pattern_dia, unit=unit), unit=unit) + + case 'rf': + pitch = float(obj.get('pitch', 2.54)) + hole_dia = float(obj['hole_dia']) + via_dia = float(obj['via_dia']) + via_drill = float(obj['via_hole_dia']) + return pb.PatternProtoArea(pitch, pitch, pb.RFGroundProto(pitch, hole_dia, clearance, via_dia, via_drill, unit=MM), unit=MM) + +@app.route('/preview.svg', methods=['POST']) +async def preview(): + obj = await request.get_json() + + unit = Inch if obj.get('units' == 'us') else MM + w = float(obj.get('width', unit(100, MM))) + h = float(obj.get('height', unit(80, MM))) + corner_radius = float(obj.get('round_corners', {}).get('radius', unit(1.5, MM))) + holes = obj.get('mounting_holes', {}) + mounting_hole_dia = float(holes.get('diameter', unit(3.2, MM))) + mounting_hole_offset = float(holes.get('offset', unit(5, MM))) + + content = deserialize(obj['children'][0], unit) + + board = pb.ProtoBoard(w, h, content, + corner_radius=corner_radius, + mounting_hole_dia=mounting_hole_dia, + mounting_hole_offset=mounting_hole_offset, + unit=unit) + return Response(str(board.pretty_svg()), mimetype='image/svg+xml') + + +if __name__ == '__main__': + app.run() + -- cgit