diff --git a/keycap.py b/keycap.py index 1c9278f..54fa515 100644 --- a/keycap.py +++ b/keycap.py @@ -1,12 +1,28 @@ -import opk -from cadquery import exporters -try: - from cq_server.ui import UI, show_object -except ModuleNotFoundError: - pass - -cap = opk.keycap() -show_object(cap) - -#exporters.export(cap, 'keycap.stl', tolerance=0.001, angularTolerance=0.05) -#exporters.export(cap, 'keycap.step') \ No newline at end of file +from opk import * +import cadquery as cq + +rows = [ + {'angle': 13, 'height': 16}, # row 0, function row + {'angle': 9, 'height': 14}, # row 1, numbers row + {'angle': 8, 'height': 12}, # row 2, QWERT + {'angle': -6, 'height': 11.5}, # row 3, ASDFG + {'angle': -8, 'height': 13}, # row 4, ZXCVB + {'angle': 0, 'height': 12.5}, # row 5, bottom row +] + +legend = [ + {'t':'⎋','ox':-9.0,'oy':2.5,'fs':12,'f':"DejaVu Sans Mono"}, + {'t':'α','ox':8.8,'oy':3.0,'fs':2,'f':"DejaVu Sans Mono"}, + {'t':'ă','ox':-5.8,'oy':-3.0,'fs':8,'f':"DejaVu Sans Mono"}, + {'t':'x','ox':7.8,'oy':-3.0,'fs':5,'f':"DejaVu Sans Mono"}, + {'t':'o','fs':10} + ] + +cap = keycap(angle=rows[5]['angle'], height=rows[5]['height'], + unitX=2, unitY=1, + convex=False, depth=2.8, + legend=legend) +cq.exporters.export(cap, 'space-penguin.stl', tolerance=0.001, angularTolerance=0.05) +if 'show_object' in locals(): + show_object(cap) + diff --git a/opk.py b/opk.py index bf295b9..5550269 100644 --- a/opk.py +++ b/opk.py @@ -1,12 +1,12 @@ """ ========================== - ██████ ██████ ██ ██ - ██ ██ ██ ██ ██ ██ - ██ ██ ██████ █████ - ██ ██ ██ ██ ██ - ██████ ██ ██ ██ + ██████ ██████ ██ ██ + ██ ██ ██ ██ ██ ██ + ██ ██ ██████ █████ + ██ ██ ██ ██ ██ + ██████ ██ ██ ██ ========================== - Open Programmatic Keycap + Open Programmatic Keycap ========================== OPK is a spherical top keycap profile developed in CadQuery @@ -16,7 +16,7 @@ !!! The profile is still highly experimental and very alpha stage. ¡¡¡ -If you use the code please give credit, if you do modifications consider +If you use the code please give credit, if you do modifications consider releasing them back to the public under a permissive open source license. Copyright (c) 2022 Matteo "Matt3o" Spinelli @@ -38,10 +38,7 @@ def keycap( depth: float = 2.8, # Scoop depth thickness: float = 1.5, # Keycap sides thickness convex: bool = False, # Is this a spacebar? - legend: str = "", # Legend - legendDepth: float = -1.0, # How deep to carve the legend, positive value makes the legend embossed - font: str = "sans-serif", - fontsize: float = 10 + legend: list[dict()]=[{}] , # Legend ): top_diff = base - top @@ -55,7 +52,7 @@ def keycap( ty = by - top_diff # if spacebar make the top less round-y - tension = .4 if convex else 1 + tension = .4 if convex else 1 # Three-section loft of rounded rectangles. Can't find a better way to do variable fillet base = ( @@ -130,7 +127,7 @@ def keycap( #show_object(tool, options={'alpha': 0.4}) keycap = keycap - tool - + # Top edge fillet keycap = keycap.edges(">Z").fillet(0.6) @@ -176,7 +173,7 @@ def keycap( .circle(2.75) .clean() ) - + stem2 = ( cq.Sketch() .push(stem_pts) @@ -201,30 +198,56 @@ def keycap( ) # Add the legend if present - if legend and legendDepth != 0: - fontPath = '' - if font.endswith((".otf", ".ttf", ".ttc")): - fontPath = font - font = '' - - legend = ( - cq.Workplane("XY").transformed(offset=cq.Vector(0, 0, height+1), rotate=cq.Vector(angle, 0, 0)) - .text(legend, fontsize, -4, font=font, fontPath=fontPath, halign="center", valign="center") - ) - bb = legend.val().BoundingBox() - # try to center the legend horizontally - legend = legend.translate((-bb.center.x, 0, 0)) - - if legendDepth < 0: - legend = legend - keycap - legend = legend.translate((0,0,legendDepth)) - keycap = keycap - legend - legend = legend - tool # this can be used to export the legend for 2 colors 3D printing - else: - tool = tool.translate((0,0,legendDepth)) - legend = legend - tool - legend = legend - keycap # use this for multi-color 3D printing - keycap = keycap + legend + if legend and len(legend)>0: + for l in legend: + legendDepth = -1.0 + if 'depth' in l: + legendDepth = l['depth'] + + if legendDepth != 0: + fontPath = '' + font = '' + if 'f' in l: + font = l['f'] + if font.endswith((".otf", ".ttf", ".ttc")): + fontPath = font + font = '' + ox = 0 + if 'ox' in l: + ox = l['ox'] + oy = 0 + if 'oy' in l: + oy = l['oy'] + t = '' + if 't' in l: + t = l['t'] + fontsize = 10 + if 'fs' in l: + fontsize = l['fs'] + halign = 'center' + if 'halign' in l: + halign = l['halign'] + valign = 'center' + if 'valign' in l: + valign = l['valign'] + leg = ( + cq.Workplane("XY").transformed(offset=cq.Vector(0, 0, height+1), rotate=cq.Vector(angle, 0, 0)) + .text(t, fontsize, -4, font=font, fontPath=fontPath, halign=halign, valign=valign) + ) + bb = leg.val().BoundingBox() + # try to center the legend horizontally + legend = leg.translate((-bb.center.x+ox, oy, 0)) + + if legendDepth < 0: + legend = legend - keycap + legend = legend.translate((0,0,legendDepth)) + keycap = keycap - legend + legend = legend - tool # this can be used to export the legend for 2 colors 3D printing + else: + tool = tool.translate((0,0,legendDepth)) + legend = legend - tool + legend = legend - keycap # use this for multi-color 3D printing + keycap = keycap + legend #show_object(legend, name="legend", options={'color': 'blue', 'alpha': 0})