From 58f8e917b0bc8481509d5946a09d64bb89343174 Mon Sep 17 00:00:00 2001 From: Alin Marin Elena Date: Wed, 31 Aug 2022 20:22:19 +0100 Subject: [PATCH 01/44] add a main and make it importable --- opk.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/opk.py b/opk.py index b3a658e..d902b4c 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 @@ -60,7 +60,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 = ( @@ -98,7 +98,7 @@ def keycap( ) .loft() ) - + # Create a body that will be carved from the main shape to create the shape if convex: tool = ( @@ -135,7 +135,7 @@ def keycap( #show_object(tool, options={'alpha': 0.4}) keycap = keycap - tool - + # Top edge fillet keycap = keycap.edges(">Z").fillet(0.5) From 1eb78bc9cafb38b93a4c9d848b394f566f47e118 Mon Sep 17 00:00:00 2001 From: Alin Marin Elena Date: Wed, 31 Aug 2022 21:12:38 +0100 Subject: [PATCH 02/44] allow negative angles for keycaps top --- opk.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/opk.py b/opk.py index d902b4c..bc03ae4 100644 --- a/opk.py +++ b/opk.py @@ -90,6 +90,9 @@ def keycap( ) # Main shape + lang = angle + if angle < 0: + lang = -angle keycap = ( cq.Workplane("XY") .placeSketch(base, @@ -191,6 +194,9 @@ def keycap( .chamfer(0.2) ) + if angle < 0 : + keycap = keycap.rotateAboutCenter((0,0,1),180.0) + # Add the legend if present if legend: legend = ( From 744ad02cdc33cdff369f9b3df1bac2afa98f6c92 Mon Sep 17 00:00:00 2001 From: Alin Marin Elena Date: Wed, 31 Aug 2022 21:17:02 +0100 Subject: [PATCH 03/44] update wish list --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 814a978..765cf16 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ It's especially suited for creating high/medium profile spherical top keycaps. ## Please contribute! -I'm new to CadQuery (CQ), Ptyhon and code based CAD in general. There's a lot of guesswork and fumbling around to get to this point, any help is very much appreciated. +I'm new to CadQuery (CQ), Ptyhon and code based CAD in general. There's a lot of guesswork and fumbling around to get to this point, any help is very much appreciated. ## Usage @@ -91,9 +91,16 @@ The code includes an example to use a DXF drawing as legend (check the comments - add 2U pos-like stabilizers - add support for logos and graphical legends - add reinforcement for big keys +- better legend centering +- secondary, tertiary, quaternary legends +- side printed legends +- add alternative stems +- allow reduced height of keycap for non 19.xx spacing +- clean up the code - automatic export script of all needed keycap for a full keyboard - output files already supported for 3D printing - online editor - add secondary/tertiary legends - add support for stems other than cherry - clean up the code +- fix negative angle top From 227f7850b6ee7cf4e420afa88e5ea794692259e4 Mon Sep 17 00:00:00 2001 From: Alin Marin Elena Date: Wed, 31 Aug 2022 21:21:53 +0100 Subject: [PATCH 04/44] make separator distance an option --- opk.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/opk.py b/opk.py index bc03ae4..52b4450 100644 --- a/opk.py +++ b/opk.py @@ -45,16 +45,16 @@ def keycap( thickness: float = 1.5, # Keycap sides thickness convex: bool = False, # Is this a spacebar? legend: str = "", # Legend - font: str = "sans-serif", - fontsize: float = 10 + font: str = "sans serif", + fontsize: float = 10, + sep: float = 19.05 # separation between keys ): - top_diff = base - top curv = min(curv, 1.9) - bx = 19.05 * unitX - (19.05 - base) - by = 19.05 * unitY - (19.05 - base) + bx = sep * unitX - (sep - base) + by = sep * unitY - (sep - base) tx = bx - top_diff ty = by - top_diff @@ -156,10 +156,10 @@ def keycap( if unitX < 2: stem_pts = [(0,0)] elif unitX < 3: - dist = 2.25 / 2 * 19.05 - 19.05 / 2 + dist = 2.25 / 2 * sep - sep / 2 stem_pts = [(0,0), (dist, 0), (-dist,0)] else: - dist = unitX / 2 * 19.05 - 19.05 / 2 + dist = unitX / 2 * sep - sep / 2 stem_pts = [(0,0), (dist, 0), (-dist,0)] stem1 = ( From 39fcf25ee005fa61db69a4570805425250e1d6e1 Mon Sep 17 00:00:00 2001 From: Alin Marin Elena Date: Thu, 1 Sep 2022 09:31:42 +0100 Subject: [PATCH 05/44] add m65 --- m1.py | 36 ++++++++++++++++++++++++++++++++++++ m4.py | 29 +++++++++++++++++++++++++++++ m65.py | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 m1.py create mode 100644 m4.py create mode 100644 m65.py diff --git a/m1.py b/m1.py new file mode 100644 index 0000000..1de0eef --- /dev/null +++ b/m1.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 + + +import cadquery as cq +from opk import * + +leg = [["⎋","1"], +["↹","q"], +["#","a"], +["⇧","\\"], +[""," "]] +lay= [[1]*2 , [1]*2,[1]*2,[1]*2,[1,1.25]] +fonts=[ + ["DejaVu Sans Mono"]+["Noto Sans"], + ["Noto Sans"]*2, + ["Noto Sans"]*2, + ["Noto Sans"]*2, + ["DejaVu Sans Mono"]+["Font Awesome 5 Brands Regular"] + ] + +i = -1 +j = -1 +angles=[9,8.5,-6,-7,0] + +for row,ll,ff in zip(leg,lay,fonts): + i += 1 + for k,l,f in zip(row,ll,ff): + print(k,l) + j += 1 + convex=False + if k == '': + convex=True + key = keycap(legend=k,angle=angles[i],font=f,convex=convex,unitX=l) + name="k{}{}{}u".format(i,j,l) + cq.exporters.export(key, name+".step") + cq.exporters.export(key, name+".stl", tolerance=0.001, angularTolerance=0.05) diff --git a/m4.py b/m4.py new file mode 100644 index 0000000..6006e37 --- /dev/null +++ b/m4.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 + + +import cadquery as cq +from opk import * + +leg = [["⎋","1"], +["↹","q"], +["#","a"], +["⇧","\\"], +["","⇓"]] +lay= [[1]*2 , [1]*2,[1]*2,[1]*2,[1,1.25]] + +i = -1 +j = -1 +angles=[9,8.5,-6,-7,0] + +for row,ll in zip(leg,lay): + i += 1 + for k,l in zip(row,ll): + print(k,l) + j += 1 + convex=False + if k == '': + convex=True + key = keycap(legend=k,angle=angles[i],font="Noto Sans",convex=convex,unitX=l) + name="k{}{}{}u".format(i,j,l) + cq.exporters.export(key, name+".step") + cq.exporters.export(key, name+".stl", tolerance=0.001, angularTolerance=0.05) diff --git a/m65.py b/m65.py new file mode 100644 index 0000000..1f8e628 --- /dev/null +++ b/m65.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 + + +import cadquery as cq +from opk import * + +leg = [["⎋","1","2","3","4","5","6","7","8","9","0","-","⌫"], +["↹","q", "w", "e","r","t","y","u","i","o","p","[","]"], +["#","a","s","d","f","g","h","j","k","l",";","'","⌅"], +["⇧","\\","z","x","c","v","b","n","m",",",".","↑","/"], +["⎈","","⇓","⎇","⇑","","","","⎇","⇧","←","↓","→"]] +lay= [[1]*13 , [1]*13,[1]*13,[1]*13,[1]*13] +rows=len(leg) +fonts=[ + ["DejaVu Sans Mono"]+["Noto Sans"]*11+["DejaVu Sans Mono"], + ["Noto Sans"]*13, + ["Noto Sans"]*13, + ["Noto Sans"]*13, + ["DejaVu Sans Mono"]+["Font Awesome 5 Brands"]+["Noto Sans"]*11 + ] +s=19.05 +m65 = cq.Assembly() +x = 0 +i = -1 +j = -1 +angles=[9,8.5,-6,-7,0] + +for row,ll,ff in zip(leg,lay,fonts): + x -= s + i += 1 + y = 0 + for k,l,f in zip(row,ll,ff): + print(k,l) + y += l*s + j += 1 + convex=False + if k == '': + convex=True + m65.add(keycap(legend=k,angle=angles[i],font=f,convex=convex),name="k{}{}".format(i,j),loc=cq.Location(cq.Vector(y,x,0))) + +cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) From db69853daa6821cde5c63f507a2fd86c32442147 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Thu, 1 Sep 2022 10:56:13 +0100 Subject: [PATCH 06/44] update --- m65.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/m65.py b/m65.py index 1f8e628..4d2cd7b 100644 --- a/m65.py +++ b/m65.py @@ -16,7 +16,7 @@ ["Noto Sans"]*13, ["Noto Sans"]*13, ["Noto Sans"]*13, - ["DejaVu Sans Mono"]+["Font Awesome 5 Brands"]+["Noto Sans"]*11 + ["DejaVu Sans Mono"]+["Font Awesome 6 Brands"]+["Noto Sans"]*11 ] s=19.05 m65 = cq.Assembly() @@ -37,5 +37,5 @@ if k == '': convex=True m65.add(keycap(legend=k,angle=angles[i],font=f,convex=convex),name="k{}{}".format(i,j),loc=cq.Location(cq.Vector(y,x,0))) - +show_object(m65, name="m65", options={"alpha": 0}) cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) From 615ffd06b2fc28947c78fc2732143a22cbe40b11 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Thu, 1 Sep 2022 12:09:40 +0100 Subject: [PATCH 07/44] some examples --- m1.py | 2 -- m65.py | 17 ++++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/m1.py b/m1.py index 1de0eef..927b59c 100644 --- a/m1.py +++ b/m1.py @@ -1,6 +1,4 @@ #!/usr/bin/env python3 - - import cadquery as cq from opk import * diff --git a/m65.py b/m65.py index 4d2cd7b..ee7c89f 100644 --- a/m65.py +++ b/m65.py @@ -18,24 +18,27 @@ ["Noto Sans"]*13, ["DejaVu Sans Mono"]+["Font Awesome 6 Brands"]+["Noto Sans"]*11 ] -s=19.05 +sx=19.05 +sy=19.05 m65 = cq.Assembly() -x = 0 +y = 0 i = -1 j = -1 angles=[9,8.5,-6,-7,0] for row,ll,ff in zip(leg,lay,fonts): - x -= s + y -= sy i += 1 - y = 0 + x = 0 for k,l,f in zip(row,ll,ff): print(k,l) - y += l*s + w = l*sx/2.0 j += 1 + x += w convex=False if k == '': convex=True - m65.add(keycap(legend=k,angle=angles[i],font=f,convex=convex),name="k{}{}".format(i,j),loc=cq.Location(cq.Vector(y,x,0))) -show_object(m65, name="m65", options={"alpha": 0}) + m65.add(keycap(legend=k,angle=angles[i],font=f,convex=convex),name="k{}{}".format(i,j),loc=cq.Location(cq.Vector(x,y,0))) + x += w cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +#show_object(m65, name="m65", options={"alpha": 0}) From 7a2fce94245ea6cb1316af320c460a17a6bbbd06 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Thu, 1 Sep 2022 14:47:53 +0100 Subject: [PATCH 08/44] some examples --- ansi.py | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ opk.py | 6 +++--- 2 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 ansi.py diff --git a/ansi.py b/ansi.py new file mode 100644 index 0000000..f83c705 --- /dev/null +++ b/ansi.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 + +import cadquery as cq +from opk import * +leg = [["⎋","1","2","3","4","5","6","7","8","9","0","-","+","⌫"], +["↹","q","w","e","r","t","y","u","i","o","p","[","]","\\"], +["Caps","a","s","d","f","g","h","j","k","l",";","'","enter"], +["⇧","z","x","c","v","b","n","m",",",".","/","shift"], +["⎈","","Alt","","Alt","Fn","Menu","Ctrl"]] +lay= [[1]*13+[2], + [1.5]+[1]*12+[1.5], + [1.75]+[1]*11+[2.25], + [2.25]+[1]*10+[2.75], + [1.25,1.25,1.25,6.25,1.25,1.25,1.25,1.25]] +fonts=[ + ["DejaVu Sans Mono"]+["Noto Sans"]*13, + ["DejaVu Sans Mono"]+["Noto Sans"]*13, + ["DejaVu Sans Mono"]+["Noto Sans"]*12, + ["DejaVu Sans Mono"]+["Noto Sans"]*10+["DejaVu Sans Mono"], + ["DejaVu Sans Mono"]+["Font Awesome 6 Brands"]+["Noto Sans"]*6 + ] +sx=19.05 +sy=19.05 +m65 = cq.Assembly() +y = 0 +i = -1 +j = -1 +angles=[9,8.5,-6,-7,0] + +for row,ll,ff in zip(leg,lay,fonts): + y -= sy + i += 1 + x = 0 + for k,l,f in zip(row,ll,ff): + print(k,l) + w = l*sx/2.0 + j += 1 + x += w + convex=False + if k == '': + convex=True + scoop = 2.5 + if k in ['f','F','j','J']: + scoop = 3.5 + m65.add(keycap(legend=k, + angle=angles[i], + font=f, + convex=convex, + depth = scoop, + unitX=l), + name="k{}{}".format(i,j), + loc=cq.Location(cq.Vector(x,y,0)) + ) + x += w + +cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +show_object(m65, name="legend", options={'color': 'red', 'alpha': 0}) diff --git a/opk.py b/opk.py index 52b4450..cf34a43 100644 --- a/opk.py +++ b/opk.py @@ -27,9 +27,9 @@ from cadquery import exporters # Prevent error when running from cli -if 'show_object' not in globals(): - def show_object(*args, **kwargs): - pass +#if 'show_object' not in globals(): +# def show_object(*args, **kwargs): +# pass def keycap( unitX: float = 1, # keycap size in unit. Standard sizes: 1, 1.25, 1.5, ... From 99d3270f3c8218d1996b3e8ea2e8ee1ea9380cc0 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Thu, 1 Sep 2022 15:46:52 +0100 Subject: [PATCH 09/44] some examples --- m65.py | 15 ++++++++++++--- preonic.py | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 preonic.py diff --git a/m65.py b/m65.py index ee7c89f..544422a 100644 --- a/m65.py +++ b/m65.py @@ -10,7 +10,6 @@ ["⇧","\\","z","x","c","v","b","n","m",",",".","↑","/"], ["⎈","","⇓","⎇","⇑","","","","⎇","⇧","←","↓","→"]] lay= [[1]*13 , [1]*13,[1]*13,[1]*13,[1]*13] -rows=len(leg) fonts=[ ["DejaVu Sans Mono"]+["Noto Sans"]*11+["DejaVu Sans Mono"], ["Noto Sans"]*13, @@ -38,7 +37,17 @@ convex=False if k == '': convex=True - m65.add(keycap(legend=k,angle=angles[i],font=f,convex=convex),name="k{}{}".format(i,j),loc=cq.Location(cq.Vector(x,y,0))) + scoop = 2.5 + if k in ['f','F','j','J']: + scoop = 3.5 + m65.add(keycap(legend=k, + angle=angles[i], + font=f, + convex=convex, + depth = scoop, + unitX=l), + name="k{}{}".format(i,j), + loc=cq.Location(cq.Vector(x,y,0))) x += w +show_object(m65, name="m65", options={"alpha": 0}) cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) -#show_object(m65, name="m65", options={"alpha": 0}) diff --git a/preonic.py b/preonic.py new file mode 100644 index 0000000..ee4ffea --- /dev/null +++ b/preonic.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python3 + +import cadquery as cq +from opk import * + +leg = [ + ["⎋","1","2","3","4","5","6","7","8","9","0","⌫"], + ["q", "w", "e","r","t","y","u","i","o","p","[","]"], + ["↹","a","s","d","f","g","h","j","k","l",";","'"], + ["⇧","z","x","c","v","b","n","m",",",".","/","⌅"], + ["⎈","⎇","","⎇","⇓","","⇑","←","↓","↑","→"]] +lay= [[1]*12, [1]*12,[1]*12,[1]*12,[1]*5+[2]+[1]*5] +fonts=[ + ["DejaVu Sans Mono"]+["Noto Sans"]*10+["DejaVu Sans Mono"], + ["Noto Sans"]*12, + ["Noto Sans"]*12, + ["Noto Sans"]*12, + ["DejaVu Sans Mono"]*11 + ] +sx=19.05 +sy=19.05 +pre = cq.Assembly() +y = 0 +i = -1 +j = -1 +angles=[9,8.5,-6,-7,0] + +for row,ll,ff in zip(leg,lay,fonts): + y -= sy + i += 1 + x = 0 + for k,l,f in zip(row,ll,ff): + print(k,l) + w = l*sx/2.0 + j += 1 + x += w + convex=False + if k == '': + convex=True + scoop = 2.5 + if k in ['f','F','j','J']: + scoop = 3.5 + pre.add(keycap(legend=k, + angle=angles[i], + font=f, + depth = scoop, + convex=convex, + unitX=l), + name="k{}{}".format(i,j), + loc=cq.Location(cq.Vector(x,y,0))) + x += w +cq.exporters.export(pre.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +show_object(pre, name="pre", options={"alpha": 0}) From 3c63280db3680ee194bdf3b35623e161ad7772d9 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Thu, 1 Sep 2022 15:50:30 +0100 Subject: [PATCH 10/44] some examples --- opk.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/opk.py b/opk.py index cf34a43..c94abbd 100644 --- a/opk.py +++ b/opk.py @@ -90,9 +90,6 @@ def keycap( ) # Main shape - lang = angle - if angle < 0: - lang = -angle keycap = ( cq.Workplane("XY") .placeSketch(base, From 0d2f3d64c52bdaa28459c7d27902f457e244b5ae Mon Sep 17 00:00:00 2001 From: alin m elena Date: Thu, 1 Sep 2022 16:13:16 +0100 Subject: [PATCH 11/44] more fixes --- planck.py | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 planck.py diff --git a/planck.py b/planck.py new file mode 100644 index 0000000..b4d19c2 --- /dev/null +++ b/planck.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 + +import cadquery as cq +from opk import * + +leg = [ + ["⎋", "q","w", "e","r","t","y","u","i","o","p","⌫"], + ["↹","a","s","d","f","g","h","j","k","l",";","'"], + ["⇧","z","x","c","v","b","n","m",",",".","/","⌅"], + ["⎈","⎇","","⎇","⇓","","⇑","←","↓","↑","→"]] +lay= [[1]*12, [1]*12,[1]*12,[1]*5+[2]+[1]*5] +fonts=[ + ["DejaVu Sans Mono"]+["Noto Sans"]*10+["DejaVu Sans Mono"], + ["Noto Sans"]*12, + ["Noto Sans"]*12, + ["DejaVu Sans Mono"]*11 + ] +sx=19.05 +sy=19.05 +planck = cq.Assembly() +y = 0 +i = -1 +j = -1 +angles=[8.5,-6,-7,0] + +for row,ll,ff in zip(leg,lay,fonts): + y -= sy + i += 1 + x = 0 + for k,l,f in zip(row,ll,ff): + print(k,l) + w = l*sx/2.0 + j += 1 + x += w + convex=False + if k == '': + convex=True + scoop = 2.5 + if k in ['f','F','j','J']: + scoop = 3.5 + planck.add(keycap(legend=k, + angle=angles[i], + font=f, + depth = scoop, + convex=convex, + unitX=l), + name="k{}{}".format(i,j), + loc=cq.Location(cq.Vector(x,y,0))) + x += w +cq.exporters.export(planck.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +show_object(planck, name="pre", options={"alpha": 0}) From c99fc24d17f4ffb2b8f3e0be92eee2139b71e61d Mon Sep 17 00:00:00 2001 From: alin m elena Date: Thu, 1 Sep 2022 16:24:29 +0100 Subject: [PATCH 12/44] more fixes --- opk.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/opk.py b/opk.py index c94abbd..f385968 100644 --- a/opk.py +++ b/opk.py @@ -191,9 +191,6 @@ def keycap( .chamfer(0.2) ) - if angle < 0 : - keycap = keycap.rotateAboutCenter((0,0,1),180.0) - # Add the legend if present if legend: legend = ( From 5ff5a61b22bfa988852e84725c1ec59189838eee Mon Sep 17 00:00:00 2001 From: alin m elena Date: Thu, 1 Sep 2022 16:34:21 +0100 Subject: [PATCH 13/44] more fixes --- planck.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/planck.py b/planck.py index b4d19c2..964b039 100644 --- a/planck.py +++ b/planck.py @@ -37,7 +37,7 @@ convex=True scoop = 2.5 if k in ['f','F','j','J']: - scoop = 3.5 + scoop = 2.5*1.05 planck.add(keycap(legend=k, angle=angles[i], font=f, From 12b32276a9b802235e5071a7f7f0cb86542910a5 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Thu, 1 Sep 2022 16:42:17 +0100 Subject: [PATCH 14/44] more fixes --- planck.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/planck.py b/planck.py index 964b039..1d17019 100644 --- a/planck.py +++ b/planck.py @@ -37,7 +37,7 @@ convex=True scoop = 2.5 if k in ['f','F','j','J']: - scoop = 2.5*1.05 + scoop = 2.5*1.2 planck.add(keycap(legend=k, angle=angles[i], font=f, From 991097ac97be44e27c610f4a728cfc9dabbec993 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Thu, 1 Sep 2022 18:41:31 +0100 Subject: [PATCH 15/44] more fixes --- m65.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/m65.py b/m65.py index 544422a..2e370eb 100644 --- a/m65.py +++ b/m65.py @@ -39,7 +39,7 @@ convex=True scoop = 2.5 if k in ['f','F','j','J']: - scoop = 3.5 + scoop = 2.5*1.2 m65.add(keycap(legend=k, angle=angles[i], font=f, @@ -49,5 +49,5 @@ name="k{}{}".format(i,j), loc=cq.Location(cq.Vector(x,y,0))) x += w +#cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) show_object(m65, name="m65", options={"alpha": 0}) -cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) From 25408637955bcb6569d0c6554d5edbc353161a5c Mon Sep 17 00:00:00 2001 From: Alin Marin Elena Date: Thu, 1 Sep 2022 21:43:24 +0100 Subject: [PATCH 16/44] add ansi68 --- ansi68.py | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 ansi68.py diff --git a/ansi68.py b/ansi68.py new file mode 100644 index 0000000..17b7a03 --- /dev/null +++ b/ansi68.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 + +import cadquery as cq +from opk import * +leg = [["⎋","1","2","3","4","5","6","7","8","9","0","-","+","⌫","`"], +["↹","q","w","e","r","t","y","u","i","o","p","[","]","\\","⌦"], +["⇪","a","s","d","f","g","h","j","k","l",";","'","enter","Up"], +["⇧","z","x","c","v","b","n","m",",",".","/","⇧","↑","Dn"], +["⎈","","⎇","","⎇","Fn","⎈","←","↓","→"]] +lay= [[1]*13+[2,1], + [1.5]+[1]*12+[1.5]+[1], + [1.75]+[1]*11+[2.25]+[1], + [2.25]+[1]*10+[1.75]+[1,1], + [1.25,1.25,1.25,6.25,1,1,1,1,1,1]] +fonts=[ + ["DejaVu Sans Mono"]+["Noto Sans"]*14, + ["DejaVu Sans Mono"]+["Noto Sans"]*14, + ["DejaVu Sans Mono"]+["Noto Sans"]*13, + ["DejaVu Sans Mono"]+["Noto Sans"]*10+["DejaVu Sans Mono"]*3, + ["DejaVu Sans Mono"]+["Font Awesome 6 Brands"]+["Noto Sans"]*4+["DejaVu Sans Mono"]*4 + ] +sx=19.05 +sy=19.05 +m65 = cq.Assembly() +y = 0 +i = -1 +j = -1 +angles=[9,8.5,-6,-7,0] + +for row,ll,ff in zip(leg,lay,fonts): + y -= sy + i += 1 + x = 0 + for k,l,f in zip(row,ll,ff): + print(k,l) + w = l*sx/2.0 + j += 1 + x += w + convex=False + if k == '': + convex=True + scoop = 2.5 + if k in ['f','F','j','J']: + scoop = 2.5*1.2 + m65.add(keycap(legend=k, + angle=angles[i], + font=f, + convex=convex, + depth = scoop, + unitX=l), + name="k{}{}".format(i,j), + loc=cq.Location(cq.Vector(x,y,0)) + ) + x += w + +cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +#show_object(m65, name="legend", options={'color': 'red', 'alpha': 0}) From 504923b01ae4edaddb5967d86a04567d0184ea47 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Fri, 2 Sep 2022 10:46:52 +0100 Subject: [PATCH 17/44] update --- ansi.py => examples/ansi.py | 0 pyproject.toml | 23 +++++++++++++++++++++++ opk.py => src/opk.py | 0 3 files changed, 23 insertions(+) rename ansi.py => examples/ansi.py (100%) create mode 100644 pyproject.toml rename opk.py => src/opk.py (100%) diff --git a/ansi.py b/examples/ansi.py similarity index 100% rename from ansi.py rename to examples/ansi.py diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..520e7e3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,23 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "opk" +version = "0.0.1" +authors = [ + { name="Matteo (Matt3o) Spinelli", email="info@matt3o.com" }, +] +description = "Open Programmatic Keycap" +readme = "README.md" +license = { file="LICENSE" } +requires-python = ">=3.7" +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: Apache-2.0", + "Operating System :: OS Independent", +] + +[project.urls] +"Homepage" = "https://github.com/cubiq/OPK" +"Bug Tracker" = "https://github.com/cubiq/OPK/issues" diff --git a/opk.py b/src/opk.py similarity index 100% rename from opk.py rename to src/opk.py From b6c11da9082ab2abf31b4fc9e5099252d4751099 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Fri, 2 Sep 2022 10:49:56 +0100 Subject: [PATCH 18/44] update --- ansi68.py => examples/ansi68.py | 0 m65.py => examples/m65.py | 0 planck.py => examples/planck.py | 0 preonic.py => examples/preonic.py | 0 m1.py | 34 ------------------------------- m4.py | 29 -------------------------- 6 files changed, 63 deletions(-) rename ansi68.py => examples/ansi68.py (100%) rename m65.py => examples/m65.py (100%) rename planck.py => examples/planck.py (100%) rename preonic.py => examples/preonic.py (100%) delete mode 100644 m1.py delete mode 100644 m4.py diff --git a/ansi68.py b/examples/ansi68.py similarity index 100% rename from ansi68.py rename to examples/ansi68.py diff --git a/m65.py b/examples/m65.py similarity index 100% rename from m65.py rename to examples/m65.py diff --git a/planck.py b/examples/planck.py similarity index 100% rename from planck.py rename to examples/planck.py diff --git a/preonic.py b/examples/preonic.py similarity index 100% rename from preonic.py rename to examples/preonic.py diff --git a/m1.py b/m1.py deleted file mode 100644 index 927b59c..0000000 --- a/m1.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python3 -import cadquery as cq -from opk import * - -leg = [["⎋","1"], -["↹","q"], -["#","a"], -["⇧","\\"], -[""," "]] -lay= [[1]*2 , [1]*2,[1]*2,[1]*2,[1,1.25]] -fonts=[ - ["DejaVu Sans Mono"]+["Noto Sans"], - ["Noto Sans"]*2, - ["Noto Sans"]*2, - ["Noto Sans"]*2, - ["DejaVu Sans Mono"]+["Font Awesome 5 Brands Regular"] - ] - -i = -1 -j = -1 -angles=[9,8.5,-6,-7,0] - -for row,ll,ff in zip(leg,lay,fonts): - i += 1 - for k,l,f in zip(row,ll,ff): - print(k,l) - j += 1 - convex=False - if k == '': - convex=True - key = keycap(legend=k,angle=angles[i],font=f,convex=convex,unitX=l) - name="k{}{}{}u".format(i,j,l) - cq.exporters.export(key, name+".step") - cq.exporters.export(key, name+".stl", tolerance=0.001, angularTolerance=0.05) diff --git a/m4.py b/m4.py deleted file mode 100644 index 6006e37..0000000 --- a/m4.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python3 - - -import cadquery as cq -from opk import * - -leg = [["⎋","1"], -["↹","q"], -["#","a"], -["⇧","\\"], -["","⇓"]] -lay= [[1]*2 , [1]*2,[1]*2,[1]*2,[1,1.25]] - -i = -1 -j = -1 -angles=[9,8.5,-6,-7,0] - -for row,ll in zip(leg,lay): - i += 1 - for k,l in zip(row,ll): - print(k,l) - j += 1 - convex=False - if k == '': - convex=True - key = keycap(legend=k,angle=angles[i],font="Noto Sans",convex=convex,unitX=l) - name="k{}{}{}u".format(i,j,l) - cq.exporters.export(key, name+".step") - cq.exporters.export(key, name+".stl", tolerance=0.001, angularTolerance=0.05) From c5ae5b7db8d9135a816747e13409b03071b9f530 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Fri, 2 Sep 2022 10:50:29 +0100 Subject: [PATCH 19/44] update --- keycap.py => examples/keycap.py | 0 src/__init__.py | 0 src/opk.egg-info/PKG-INFO | 322 ++++++++++++++++++++++++++ src/opk.egg-info/SOURCES.txt | 9 + src/opk.egg-info/dependency_links.txt | 1 + src/opk.egg-info/top_level.txt | 2 + 6 files changed, 334 insertions(+) rename keycap.py => examples/keycap.py (100%) create mode 100644 src/__init__.py create mode 100644 src/opk.egg-info/PKG-INFO create mode 100644 src/opk.egg-info/SOURCES.txt create mode 100644 src/opk.egg-info/dependency_links.txt create mode 100644 src/opk.egg-info/top_level.txt diff --git a/keycap.py b/examples/keycap.py similarity index 100% rename from keycap.py rename to examples/keycap.py diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/opk.egg-info/PKG-INFO b/src/opk.egg-info/PKG-INFO new file mode 100644 index 0000000..99e982f --- /dev/null +++ b/src/opk.egg-info/PKG-INFO @@ -0,0 +1,322 @@ +Metadata-Version: 2.1 +Name: opk +Version: 0.0.1 +Summary: Open Programmatic Keycap +Author-email: "Matteo (Matt3o) Spinelli" +License: Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2022 Matteo "Matt3o" Spinelli + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +Project-URL: Homepage, https://github.com/cubiq/OPK +Project-URL: Bug Tracker, https://github.com/cubiq/OPK/issues +Classifier: Programming Language :: Python :: 3 +Classifier: License :: OSI Approved :: Apache-2.0 +Classifier: Operating System :: OS Independent +Requires-Python: >=3.7 +Description-Content-Type: text/markdown +License-File: LICENSE + +# OPK + +OPK is a spherical top keycap profile developed in [CadQuery](https://github.com/CadQuery/cadquery) and released under the very permissive Apache License 2.0. + +It's especially suited for creating high/medium profile spherical top keycaps. + +**The profile is still highly experimental and very alpha stage.** + +## Please contribute! + +I'm new to CadQuery (CQ), Ptyhon and code based CAD in general. There's a lot of guesswork and fumbling around to get to this point, any help is very much appreciated. + +## Usage + +If you are new to CQ, the easiest is to download a [CQ-Editor](https://github.com/CadQuery/CQ-editor) pre-built package (I use the latest development release), load the `keycap.py` file and hit **Render**! + +The main ``keycap`` function in the ``opk.py`` file has sane defaults and parameters are sufficiently commented. + +The *export* directory includes a few examples in both STL and STEP format. + +Some examples: + +**Create the default 1u keycap** +``` +cap = keycap() +show_object(cap) +``` + +![1U keycap](imgs/1U_default.jpg "1U keycap") + +**Create a 1.5U keycap** +``` +cap = keycap(unitX=1.5) +show_object(cap) +``` + +![1.5U keycap](imgs/1.5U.jpg "1.5U keycap") + +**Create a flat top (not angled) 2U keycap** +``` +cap = keycap(unitX=2, angle=0) +show_object(cap) +``` + +![2U flat top](imgs/2U_flat.jpg "2U flat top") + +**Create a 4U spacebar** +``` +cap = keycap(unitX=4, angle=1, convex=True) +show_object(cap) +``` + +![4U spacebar](imgs/4U_spacebar.jpg "4U spacebar") + +**A 2.25U Return key in system-default-serif font** +``` +cap = keycap(unitX=2.25, legend="Return", font="serif", fontsize=8) +show_object(cap) +``` + +![Return](imgs/2.25U_Return.jpg "Return") + +## Save and Export results + +CQ can export in many formats including STEP and STL. The easiest way to export an STL in CQ-Editor is by selecting the model in the 3D view and then clicking on *Tools > Export as STL*. + +Of course it can be done programmatically and that can make exporting an entire keyboard very easy. The following code exports an STL and a STEP file. + +``` +cap = keycap() +show_object(cap) + +exporters.export(cap, 'keycap.stl', tolerance=0.001, angularTolerance=0.05) +exporters.export(cap, 'keycap.step') +``` + +## Notes / Recommendations + +Default angle is 7deg that should be a good starting point for Row 3. + +For spacebars a small angle (0-4deg) is recommended. + +Not all fonts will be centered correctly, choose carefully. I'll add some way to better center the legends. + +The code includes an example to use a DXF drawing as legend (check the comments at the end of the keycap function). + +## TODO +- add support for vertical key +- add stepped key +- add ISO Enter +- add 2U pos-like stabilizers +- add support for logos and graphical legends +- add reinforcement for big keys +- better legend centering +- secondary, tertiary, quaternary legends +- side printed legends +- add alternative stems +- allow reduced height of keycap for non 19.xx spacing +- clean up the code +- automatic export script of all needed keycap for a full keyboard +- output files already supported for 3D printing +- online editor +- add secondary/tertiary legends +- add support for stems other than cherry +- clean up the code +- fix negative angle top diff --git a/src/opk.egg-info/SOURCES.txt b/src/opk.egg-info/SOURCES.txt new file mode 100644 index 0000000..48a9c42 --- /dev/null +++ b/src/opk.egg-info/SOURCES.txt @@ -0,0 +1,9 @@ +LICENSE +README.md +pyproject.toml +src/__init__.py +src/opk.py +src/opk.egg-info/PKG-INFO +src/opk.egg-info/SOURCES.txt +src/opk.egg-info/dependency_links.txt +src/opk.egg-info/top_level.txt \ No newline at end of file diff --git a/src/opk.egg-info/dependency_links.txt b/src/opk.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/opk.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/src/opk.egg-info/top_level.txt b/src/opk.egg-info/top_level.txt new file mode 100644 index 0000000..069ee60 --- /dev/null +++ b/src/opk.egg-info/top_level.txt @@ -0,0 +1,2 @@ +__init__ +opk From cc113813ef6942d3f9d22f9fd8b77c860b4b0463 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Fri, 2 Sep 2022 10:50:46 +0100 Subject: [PATCH 20/44] update --- src/opk.egg-info/PKG-INFO | 322 -------------------------- src/opk.egg-info/SOURCES.txt | 9 - src/opk.egg-info/dependency_links.txt | 1 - src/opk.egg-info/top_level.txt | 2 - 4 files changed, 334 deletions(-) delete mode 100644 src/opk.egg-info/PKG-INFO delete mode 100644 src/opk.egg-info/SOURCES.txt delete mode 100644 src/opk.egg-info/dependency_links.txt delete mode 100644 src/opk.egg-info/top_level.txt diff --git a/src/opk.egg-info/PKG-INFO b/src/opk.egg-info/PKG-INFO deleted file mode 100644 index 99e982f..0000000 --- a/src/opk.egg-info/PKG-INFO +++ /dev/null @@ -1,322 +0,0 @@ -Metadata-Version: 2.1 -Name: opk -Version: 0.0.1 -Summary: Open Programmatic Keycap -Author-email: "Matteo (Matt3o) Spinelli" -License: Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright 2022 Matteo "Matt3o" Spinelli - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -Project-URL: Homepage, https://github.com/cubiq/OPK -Project-URL: Bug Tracker, https://github.com/cubiq/OPK/issues -Classifier: Programming Language :: Python :: 3 -Classifier: License :: OSI Approved :: Apache-2.0 -Classifier: Operating System :: OS Independent -Requires-Python: >=3.7 -Description-Content-Type: text/markdown -License-File: LICENSE - -# OPK - -OPK is a spherical top keycap profile developed in [CadQuery](https://github.com/CadQuery/cadquery) and released under the very permissive Apache License 2.0. - -It's especially suited for creating high/medium profile spherical top keycaps. - -**The profile is still highly experimental and very alpha stage.** - -## Please contribute! - -I'm new to CadQuery (CQ), Ptyhon and code based CAD in general. There's a lot of guesswork and fumbling around to get to this point, any help is very much appreciated. - -## Usage - -If you are new to CQ, the easiest is to download a [CQ-Editor](https://github.com/CadQuery/CQ-editor) pre-built package (I use the latest development release), load the `keycap.py` file and hit **Render**! - -The main ``keycap`` function in the ``opk.py`` file has sane defaults and parameters are sufficiently commented. - -The *export* directory includes a few examples in both STL and STEP format. - -Some examples: - -**Create the default 1u keycap** -``` -cap = keycap() -show_object(cap) -``` - -![1U keycap](imgs/1U_default.jpg "1U keycap") - -**Create a 1.5U keycap** -``` -cap = keycap(unitX=1.5) -show_object(cap) -``` - -![1.5U keycap](imgs/1.5U.jpg "1.5U keycap") - -**Create a flat top (not angled) 2U keycap** -``` -cap = keycap(unitX=2, angle=0) -show_object(cap) -``` - -![2U flat top](imgs/2U_flat.jpg "2U flat top") - -**Create a 4U spacebar** -``` -cap = keycap(unitX=4, angle=1, convex=True) -show_object(cap) -``` - -![4U spacebar](imgs/4U_spacebar.jpg "4U spacebar") - -**A 2.25U Return key in system-default-serif font** -``` -cap = keycap(unitX=2.25, legend="Return", font="serif", fontsize=8) -show_object(cap) -``` - -![Return](imgs/2.25U_Return.jpg "Return") - -## Save and Export results - -CQ can export in many formats including STEP and STL. The easiest way to export an STL in CQ-Editor is by selecting the model in the 3D view and then clicking on *Tools > Export as STL*. - -Of course it can be done programmatically and that can make exporting an entire keyboard very easy. The following code exports an STL and a STEP file. - -``` -cap = keycap() -show_object(cap) - -exporters.export(cap, 'keycap.stl', tolerance=0.001, angularTolerance=0.05) -exporters.export(cap, 'keycap.step') -``` - -## Notes / Recommendations - -Default angle is 7deg that should be a good starting point for Row 3. - -For spacebars a small angle (0-4deg) is recommended. - -Not all fonts will be centered correctly, choose carefully. I'll add some way to better center the legends. - -The code includes an example to use a DXF drawing as legend (check the comments at the end of the keycap function). - -## TODO -- add support for vertical key -- add stepped key -- add ISO Enter -- add 2U pos-like stabilizers -- add support for logos and graphical legends -- add reinforcement for big keys -- better legend centering -- secondary, tertiary, quaternary legends -- side printed legends -- add alternative stems -- allow reduced height of keycap for non 19.xx spacing -- clean up the code -- automatic export script of all needed keycap for a full keyboard -- output files already supported for 3D printing -- online editor -- add secondary/tertiary legends -- add support for stems other than cherry -- clean up the code -- fix negative angle top diff --git a/src/opk.egg-info/SOURCES.txt b/src/opk.egg-info/SOURCES.txt deleted file mode 100644 index 48a9c42..0000000 --- a/src/opk.egg-info/SOURCES.txt +++ /dev/null @@ -1,9 +0,0 @@ -LICENSE -README.md -pyproject.toml -src/__init__.py -src/opk.py -src/opk.egg-info/PKG-INFO -src/opk.egg-info/SOURCES.txt -src/opk.egg-info/dependency_links.txt -src/opk.egg-info/top_level.txt \ No newline at end of file diff --git a/src/opk.egg-info/dependency_links.txt b/src/opk.egg-info/dependency_links.txt deleted file mode 100644 index 8b13789..0000000 --- a/src/opk.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/opk.egg-info/top_level.txt b/src/opk.egg-info/top_level.txt deleted file mode 100644 index 069ee60..0000000 --- a/src/opk.egg-info/top_level.txt +++ /dev/null @@ -1,2 +0,0 @@ -__init__ -opk From 85ad7c21a1d8beee50aeb4d3474430c88743df23 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Fri, 2 Sep 2022 11:34:49 +0100 Subject: [PATCH 21/44] update --- examples/planck.py | 2 +- examples/test.scad | 1 + pyproject.toml | 12 +++++++++--- src/opk.py | 6 +++--- 4 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 examples/test.scad diff --git a/examples/planck.py b/examples/planck.py index 1d17019..50556a0 100644 --- a/examples/planck.py +++ b/examples/planck.py @@ -48,4 +48,4 @@ loc=cq.Location(cq.Vector(x,y,0))) x += w cq.exporters.export(planck.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) -show_object(planck, name="pre", options={"alpha": 0}) +#show_object(planck, name="pre", options={"alpha": 0}) diff --git a/examples/test.scad b/examples/test.scad new file mode 100644 index 0000000..4f51465 --- /dev/null +++ b/examples/test.scad @@ -0,0 +1 @@ +import("keycaps.stl"); diff --git a/pyproject.toml b/pyproject.toml index 520e7e3..8592d06 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,23 +1,29 @@ [build-system] -requires = ["setuptools>=61.0"] +requires = ["setuptools>=61.0", "setuptools-scm"] build-backend = "setuptools.build_meta" [project] name = "opk" -version = "0.0.1" +version = "0.0.5" authors = [ { name="Matteo (Matt3o) Spinelli", email="info@matt3o.com" }, ] + description = "Open Programmatic Keycap" readme = "README.md" license = { file="LICENSE" } requires-python = ">=3.7" + classifiers = [ "Programming Language :: Python :: 3", - "License :: OSI Approved :: Apache-2.0", + "License :: OSI Approved :: Apache Software License", "Operating System :: OS Independent", ] +dependencies = [ + "cadquery==2.2.0b1", +] + [project.urls] "Homepage" = "https://github.com/cubiq/OPK" "Bug Tracker" = "https://github.com/cubiq/OPK/issues" diff --git a/src/opk.py b/src/opk.py index f385968..cf9fcf2 100644 --- a/src/opk.py +++ b/src/opk.py @@ -27,9 +27,9 @@ from cadquery import exporters # Prevent error when running from cli -#if 'show_object' not in globals(): -# def show_object(*args, **kwargs): -# pass +if 'show_object' not in globals(): + def show_object(*args, **kwargs): + pass def keycap( unitX: float = 1, # keycap size in unit. Standard sizes: 1, 1.25, 1.5, ... From bd73aa04a7bfd19dccc6026682a80b5c2e00b84a Mon Sep 17 00:00:00 2001 From: alin m elena Date: Fri, 2 Sep 2022 11:47:13 +0100 Subject: [PATCH 22/44] update --- examples/rows.py | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 examples/rows.py diff --git a/examples/rows.py b/examples/rows.py new file mode 100644 index 0000000..65fc1fd --- /dev/null +++ b/examples/rows.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 + + +import cadquery as cq +from opk import * + +leg = [["⎋"],["⎋"], +["↹"], +["⇪","d","f","g"], +["⇧"], +["⎈"], +[""]] +lay= [[1],[1],[1.5],[1.75]+[1]*3,[2.25],[1.25],[2]] +fonts=[ + ["DejaVu Sans Mono"], + ["DejaVu Sans Mono"], + ["DejaVu Sans Mono"], + ["DejaVu Sans Mono"]*4, + ["DejaVu Sans Mono"], + ["DejaVu Sans Mono"], + ["DejaVu Sans Mono"] + ] +sx = 19.05 +sy = 19.05 +rows = cq.Assembly() +y = 0 +i = -1 +j = -1 +angles = [13.5,9,8.5,-6,-7,0,0] + +for row,ll,ff in zip(leg,lay,fonts): + y -= sy + i += 1 + x = 0 + for k,l,f in zip(row,ll,ff): + print(k,l) + w = l*sx/2.0 + j += 1 + x += w + convex = False + if k == '': + convex = True + scoop = 2.5 + if k in ['f','F','j','J']: + scoop = 2.5*1.2 + rows.add(keycap(legend=k, + angle=angles[i], + font=f, + convex=convex, + depth=scoop, + unitX=l), + name="k{}{}".format(i,j), + loc=cq.Location(cq.Vector(x,y,0))) + x += w +cq.exporters.export(rows.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +#show_object(rows, name="rows", options={"alpha": 0}) From 5a1b90287f94687cbfd09d01a31e4c4525bb8b04 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Fri, 2 Sep 2022 12:09:54 +0100 Subject: [PATCH 23/44] update --- examples/rows.py | 2 +- pyproject.toml | 2 +- src/opk.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/rows.py b/examples/rows.py index 65fc1fd..9546e51 100644 --- a/examples/rows.py +++ b/examples/rows.py @@ -53,4 +53,4 @@ loc=cq.Location(cq.Vector(x,y,0))) x += w cq.exporters.export(rows.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) -#show_object(rows, name="rows", options={"alpha": 0}) +show_object(rows, name="rows", options={"alpha": 0}) diff --git a/pyproject.toml b/pyproject.toml index 8592d06..c505819 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "opk" -version = "0.0.5" +version = "0.0.6" authors = [ { name="Matteo (Matt3o) Spinelli", email="info@matt3o.com" }, ] diff --git a/src/opk.py b/src/opk.py index cf9fcf2..f385968 100644 --- a/src/opk.py +++ b/src/opk.py @@ -27,9 +27,9 @@ from cadquery import exporters # Prevent error when running from cli -if 'show_object' not in globals(): - def show_object(*args, **kwargs): - pass +#if 'show_object' not in globals(): +# def show_object(*args, **kwargs): +# pass def keycap( unitX: float = 1, # keycap size in unit. Standard sizes: 1, 1.25, 1.5, ... From 1343696209c4eb247c919d9fbb8e596e1a639add Mon Sep 17 00:00:00 2001 From: Alin Marin Elena Date: Sat, 3 Sep 2022 08:02:41 +0100 Subject: [PATCH 24/44] update --- src/opk.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/opk.py b/src/opk.py index f385968..5f22240 100644 --- a/src/opk.py +++ b/src/opk.py @@ -45,16 +45,18 @@ def keycap( thickness: float = 1.5, # Keycap sides thickness convex: bool = False, # Is this a spacebar? legend: str = "", # Legend - font: str = "sans serif", + font: str = "sans-serif", fontsize: float = 10, - sep: float = 19.05 # separation between keys + sx: float = 19.05, # distance between switches on x + sy: float = 19.05 # distance between switches on y ): + top_diff = base - top curv = min(curv, 1.9) - bx = sep * unitX - (sep - base) - by = sep * unitY - (sep - base) + bx = sx * unitX - (sx - base) + by = sy * unitY - (sy - base) tx = bx - top_diff ty = by - top_diff @@ -153,10 +155,10 @@ def keycap( if unitX < 2: stem_pts = [(0,0)] elif unitX < 3: - dist = 2.25 / 2 * sep - sep / 2 + dist = 2.25 / 2 * sx - sx / 2 stem_pts = [(0,0), (dist, 0), (-dist,0)] else: - dist = unitX / 2 * sep - sep / 2 + dist = unitX / 2 * sx - sx / 2 stem_pts = [(0,0), (dist, 0), (-dist,0)] stem1 = ( From 1b74d0276545a4513b53b10dfc4a51e5b2a998d5 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Sat, 3 Sep 2022 17:06:22 +0100 Subject: [PATCH 25/44] more fixes --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c505819..21102d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "opk" -version = "0.0.6" +version = "0.0.7" authors = [ { name="Matteo (Matt3o) Spinelli", email="info@matt3o.com" }, ] From bf08eba1a60c7574bf43f7ca8c993845185f3871 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Sat, 3 Sep 2022 17:10:08 +0100 Subject: [PATCH 26/44] more fixes --- src/opk.py | 65 ++++++++++++++++++++++++------------------------------ 1 file changed, 29 insertions(+), 36 deletions(-) diff --git a/src/opk.py b/src/opk.py index 5f22240..d6ba5f6 100644 --- a/src/opk.py +++ b/src/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 @@ -26,11 +26,6 @@ import cadquery as cq from cadquery import exporters -# Prevent error when running from cli -#if 'show_object' not in globals(): -# def show_object(*args, **kwargs): -# pass - def keycap( unitX: float = 1, # keycap size in unit. Standard sizes: 1, 1.25, 1.5, ... unitY: float = 1, @@ -41,28 +36,26 @@ def keycap( tFillet: float = 4, # Fillet at the top height: float = 13, # Height of the keycap before cutting the scoop (final height is lower) angle: float = 7, # Angle of the top surface - depth: float = 2.5, # Scoop depth + depth: float = 2.4, # Scoop depth thickness: float = 1.5, # Keycap sides thickness convex: bool = False, # Is this a spacebar? legend: str = "", # Legend font: str = "sans-serif", - fontsize: float = 10, - sx: float = 19.05, # distance between switches on x - sy: float = 19.05 # distance between switches on y + fontsize: float = 10 ): top_diff = base - top curv = min(curv, 1.9) - bx = sx * unitX - (sx - base) - by = sy * unitY - (sy - base) + bx = 19.05 * unitX - (19.05 - base) + by = 19.05 * unitY - (19.05 - base) tx = bx - top_diff 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 = ( @@ -101,7 +94,7 @@ def keycap( .loft() ) - # Create a body that will be carved from the main shape to create the shape + # Create a body that will be carved from the main shape to create the top scoop if convex: tool = ( cq.Workplane("YZ").transformed(offset=cq.Vector(0, height-2.1, -bx/2), rotate=cq.Vector(0, 0, angle)) @@ -114,30 +107,30 @@ def keycap( ) else: tool = ( - cq.Workplane("YZ").transformed(offset=cq.Vector(0, height+1, bx/2), rotate=cq.Vector(0, 0, angle)) - .moveTo(-by/2+2,0) - .threePointArc((0, min(-0.1, -depth+1)), (by/2-2, 0)) - .lineTo(by/2-2, 10) - .lineTo(-by/2+2, 10) + cq.Workplane("YZ").transformed(offset=cq.Vector(0, height, bx/2), rotate=cq.Vector(0, 0, angle)) + .moveTo(-by/2+2.4,0) + .threePointArc((0, min(-0.1, -depth+1)), (by/2-2.4, 0)) + .lineTo(by/2, height) + .lineTo(-by/2, height) .close() .workplane(offset=-bx/2) .moveTo(-by/2, -1) .threePointArc((0, -depth), (by/2, -1)) - .lineTo(by/2, 10) - .lineTo(-by/2, 10) + .lineTo(by/2, height) + .lineTo(-by/2, height) .close() .workplane(offset=-bx/2) - .moveTo(-by/2+2, 0) - .threePointArc((0, min(-0.1, -depth+1)), (by/2-2, 0)) - .lineTo(by/2-2, 10) - .lineTo(-by/2+2, 10) + .moveTo(-by/2+2.4, 0) + .threePointArc((0, min(-0.1, -depth+1)), (by/2-2.4, 0)) + .lineTo(by/2, height) + .lineTo(-by/2, height) .close() .loft(combine=False) ) #show_object(tool, options={'alpha': 0.4}) keycap = keycap - tool - + # Top edge fillet keycap = keycap.edges(">Z").fillet(0.5) @@ -155,10 +148,10 @@ def keycap( if unitX < 2: stem_pts = [(0,0)] elif unitX < 3: - dist = 2.25 / 2 * sx - sx / 2 + dist = 2.25 / 2 * 19.05 - 19.05 / 2 stem_pts = [(0,0), (dist, 0), (-dist,0)] else: - dist = unitX / 2 * sx - sx / 2 + dist = unitX / 2 * 19.05 - 19.05 / 2 stem_pts = [(0,0), (dist, 0), (-dist,0)] stem1 = ( @@ -188,7 +181,7 @@ def keycap( .extrude("next") .faces("Z[1]").edges("|X or |Y") .chamfer(0.2) ) @@ -197,7 +190,7 @@ def keycap( if legend: legend = ( cq.Workplane("XY").transformed(offset=cq.Vector(0, 0, height+1), rotate=cq.Vector(angle, 0, 0)) - .text(legend, fontsize, -4, font=font, halign="center") + .text(legend, fontsize, -4, font=font, halign="center", valign="center") ) bb = legend.val().BoundingBox() # try to center the legend horizontally From 394c22d7c52a21e7bd0f9970865ce1e99312f8f9 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Sat, 3 Sep 2022 23:08:25 +0100 Subject: [PATCH 27/44] more fixes --- examples/ansi.py | 29 ++++++++------ examples/ansi68.py | 29 ++++++++------ examples/full-ansi.py | 89 +++++++++++++++++++++++++++++++++++++++++++ examples/keycap.py | 4 +- examples/m65.py | 26 ++++++++----- examples/planck.py | 21 ++++++---- examples/preonic.py | 27 ++++++++----- examples/tkl.py | 89 +++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/opk.py | 18 ++++----- 10 files changed, 273 insertions(+), 61 deletions(-) create mode 100644 examples/full-ansi.py create mode 100644 examples/tkl.py diff --git a/examples/ansi.py b/examples/ansi.py index f83c705..2bb7923 100644 --- a/examples/ansi.py +++ b/examples/ansi.py @@ -2,10 +2,10 @@ import cadquery as cq from opk import * -leg = [["⎋","1","2","3","4","5","6","7","8","9","0","-","+","⌫"], -["↹","q","w","e","r","t","y","u","i","o","p","[","]","\\"], -["Caps","a","s","d","f","g","h","j","k","l",";","'","enter"], -["⇧","z","x","c","v","b","n","m",",",".","/","shift"], +leg = [["⎋","!\n1","\"\n2","£\n3","$\n4","%\n5","^\n6","&\n7","*\n8","(\n9",")\n0","_\n-","=\n+","⌫"], +["↹","q","w","e","r","t","y","u","i","o","p","{\n[","}\n]","|\n\\"], +["Caps","a","s","d","f","g","h","j","k","l",":\n;","@\n'","enter"], +["⇧","z","x","c","v","b","n","m","<\n,",">\n.","?\n/","⇧"], ["⎈","","Alt","","Alt","Fn","Menu","Ctrl"]] lay= [[1]*13+[2], [1.5]+[1]*12+[1.5], @@ -13,11 +13,11 @@ [2.25]+[1]*10+[2.75], [1.25,1.25,1.25,6.25,1.25,1.25,1.25,1.25]] fonts=[ - ["DejaVu Sans Mono"]+["Noto Sans"]*13, - ["DejaVu Sans Mono"]+["Noto Sans"]*13, - ["DejaVu Sans Mono"]+["Noto Sans"]*12, - ["DejaVu Sans Mono"]+["Noto Sans"]*10+["DejaVu Sans Mono"], - ["DejaVu Sans Mono"]+["Font Awesome 6 Brands"]+["Noto Sans"]*6 + ["DejaVu Sans Mono"]*14, + ["DejaVu Sans Mono"]*14, + ["DejaVu Sans Mono"]*13, + ["DejaVu Sans Mono"]*12, + ["DejaVu Sans Mono"]+["Font Awesome 6 Brands"]+["DejaVu Sans Mono"]*6 ] sx=19.05 sy=19.05 @@ -26,6 +26,7 @@ i = -1 j = -1 angles=[9,8.5,-6,-7,0] +vfs=[0,9,7,6,4.5,4.5] for row,ll,ff in zip(leg,lay,fonts): y -= sy @@ -41,17 +42,23 @@ convex=True scoop = 2.5 if k in ['f','F','j','J']: - scoop = 3.5 + scoop = 2.5*1.2 + fs=3 + if len(k)<=5: + fs=vfs[len(k)] + if (len(k.split("\n"))==2): + fs = 4.5 m65.add(keycap(legend=k, angle=angles[i], font=f, convex=convex, depth = scoop, + fontsize = fs, unitX=l), name="k{}{}".format(i,j), loc=cq.Location(cq.Vector(x,y,0)) ) x += w -cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +#cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) show_object(m65, name="legend", options={'color': 'red', 'alpha': 0}) diff --git a/examples/ansi68.py b/examples/ansi68.py index 17b7a03..f7a57e6 100644 --- a/examples/ansi68.py +++ b/examples/ansi68.py @@ -2,10 +2,10 @@ import cadquery as cq from opk import * -leg = [["⎋","1","2","3","4","5","6","7","8","9","0","-","+","⌫","`"], -["↹","q","w","e","r","t","y","u","i","o","p","[","]","\\","⌦"], -["⇪","a","s","d","f","g","h","j","k","l",";","'","enter","Up"], -["⇧","z","x","c","v","b","n","m",",",".","/","⇧","↑","Dn"], +leg = [["⎋","!\n1","\"\n2","£\n3","$\n4","%\n5","^\n6","&\n7","*\n8","(\n9",")\n0","_\n-","=\n+","⌫","¬\n`"], +["↹","q","w","e","r","t","y","u","i","o","p","[","]","|\n\\","⌦"], +["⇪","a","s","d","f","g","h","j","k","l",":\n;","@\n'","enter","Pg\nUp"], +["⇧","z","x","c","v","b","n","m","<\n,",">\n.","?\n/","⇧","↑","Pg\nDn"], ["⎈","","⎇","","⎇","Fn","⎈","←","↓","→"]] lay= [[1]*13+[2,1], [1.5]+[1]*12+[1.5]+[1], @@ -13,11 +13,11 @@ [2.25]+[1]*10+[1.75]+[1,1], [1.25,1.25,1.25,6.25,1,1,1,1,1,1]] fonts=[ - ["DejaVu Sans Mono"]+["Noto Sans"]*14, - ["DejaVu Sans Mono"]+["Noto Sans"]*14, - ["DejaVu Sans Mono"]+["Noto Sans"]*13, - ["DejaVu Sans Mono"]+["Noto Sans"]*10+["DejaVu Sans Mono"]*3, - ["DejaVu Sans Mono"]+["Font Awesome 6 Brands"]+["Noto Sans"]*4+["DejaVu Sans Mono"]*4 + ["DejaVu Sans Mono"]*15, + ["DejaVu Sans Mono"]*15, + ["DejaVu Sans Mono"]*14, + ["DejaVu Sans Mono"]*14, + ["DejaVu Sans Mono"]+["Font Awesome 6 Brands"]+["DejaVu Sans Mono"]*8 ] sx=19.05 sy=19.05 @@ -26,6 +26,7 @@ i = -1 j = -1 angles=[9,8.5,-6,-7,0] +vfs=[0,9,7,6,4.5,4.5] for row,ll,ff in zip(leg,lay,fonts): y -= sy @@ -42,16 +43,22 @@ scoop = 2.5 if k in ['f','F','j','J']: scoop = 2.5*1.2 + fs=3 + if len(k)<=5: + fs=vfs[len(k)] + if (len(k.split("\n"))==2): + fs = 4.5 m65.add(keycap(legend=k, angle=angles[i], font=f, convex=convex, depth = scoop, + fontsize = fs, unitX=l), name="k{}{}".format(i,j), loc=cq.Location(cq.Vector(x,y,0)) ) x += w -cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) -#show_object(m65, name="legend", options={'color': 'red', 'alpha': 0}) +#cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +show_object(m65, name="legend", options={'color': 'red', 'alpha': 0}) diff --git a/examples/full-ansi.py b/examples/full-ansi.py new file mode 100644 index 0000000..48d1769 --- /dev/null +++ b/examples/full-ansi.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 + +import cadquery as cq +from opk import * +leg = [ ["⎋","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12", "Prn\nScr","Scr\nLock","Pause"], + ["¬\n`","!\n1","\"\n2","£\n3","$\n4","%\n5","^\n6","&\n7","*\n8","(\n9",")\n0","_\n-","=\n+","⌫","Ins","Home","Pg\nUp","Num","/","*","-"], +["↹","q","w","e","r","t","y","u","i","o","p","[","]","|\n\\","Del","End","Pg\nDn","7","8","9","+"], +["⇪","a","s","d","f","g","h","j","k","l",";","'","enter","4","5","6","+"], +["⇧","z","x","c","v","b","n","m",",",".","/","⇧","↑","1","2","3","Ent"], +["⎈","","Alt","","Alt","Fn","Mnu","Ctrl","←","↓","→","0",".","Ent"]] +lay= [[1]*16, + [1]*13+[2]+[1]*7, + [1.5]+[1]*12+[1.5]+[1]*7, + [1.75]+[1]*11+[2.25]+[1]*4, + [2.25]+[1]*10+[2.75]+[1]*5, + [1.25,1.25,1.25,6.25,1.25,1.25,1.25,1.25,1,1,1,2,1,1]] +fonts=[ + ["DejaVu Sans Mono"]*16, + ["DejaVu Sans Mono"]*21, + ["DejaVu Sans Mono"]*21, + ["DejaVu Sans Mono"]*17, + ["DejaVu Sans Mono"]*17, + ["DejaVu Sans Mono"]+["Font Awesome 6 Brands"]+["Noto Sans"]*6+["DejaVu Sans Mono"]*6 + ] + +offx=[[0,1,0,0,0]+[0.5,0,0,0]+[0.5,0,0,0]+[0.5,0,0], + [0.0]*14+[0.5,0,0,0.5,0.0,0,0], + [0.0]*14+[0.5,0,0,0.5,0,0,0], + [0.0]*13+[4,0,0,0], + [0.0]*12+[1.5,1.5,0,0,0], + [0.0]*8+[0.5,0,0,0.5,0,0] + ] +offy=[[0.5]+[0]*15, + [0]*21, + [0]*21, + [0]*17, + [0]*17, + [0]*14 + ] + +sx=19.05 +sy=19.05 + +m65 = cq.Assembly() + +y = 0 +i = -1 +j = -1 + +angles=[13.5,9,8.5,-6,-7,0] +vfs=[0,9,7,6,4.5,4.5] + +for row,ll,ff,ofx,ofy in zip(leg,lay,fonts,offx,offy): + + i += 1 + y = -(i+1)*sy + + x = 0 + for k,l,f,ox,oy in zip(row,ll,ff,ofx,ofy): + print(k,len(k),l,f) + w = l*sx/2.0 + j += 1 + x += w + ox*sx + y += oy*sy + convex=False + if k == '': + convex=True + scoop = 2.5 + if k in ['f','F','j','J']: + scoop = 2.5*1.2 + fs=3 + if len(k)<=5: + fs=vfs[len(k)] + if (len(k.split("\n"))==2): + fs = 4.5 + m65.add(keycap(legend=k, + angle=angles[i], + font=f, + convex=convex, + depth = scoop, + fontsize=fs, + unitX=l), + name="k{}{}".format(i,j), + loc=cq.Location(cq.Vector(x,y,0)) + ) + x += w + +#cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +show_object(m65, name="legend", options={'color': 'red', 'alpha': 0}) diff --git a/examples/keycap.py b/examples/keycap.py index aa3def4..b90af47 100644 --- a/examples/keycap.py +++ b/examples/keycap.py @@ -1,7 +1,7 @@ import opk -cap = opk.keycap() +cap = opk.keycap(legend="shift",unitX=1,unitY=2) show_object(cap, name="keycap", options={"alpha": 0}) #exporters.export(cap, 'keycap.stl', tolerance=0.001, angularTolerance=0.05) -#exporters.export(cap, 'keycap.step') \ No newline at end of file +#exporters.export(cap, 'keycap.step') diff --git a/examples/m65.py b/examples/m65.py index 2e370eb..0493ebc 100644 --- a/examples/m65.py +++ b/examples/m65.py @@ -4,18 +4,18 @@ import cadquery as cq from opk import * -leg = [["⎋","1","2","3","4","5","6","7","8","9","0","-","⌫"], -["↹","q", "w", "e","r","t","y","u","i","o","p","[","]"], -["#","a","s","d","f","g","h","j","k","l",";","'","⌅"], -["⇧","\\","z","x","c","v","b","n","m",",",".","↑","/"], +leg = [["⎋ `\n ¬","!\n1","\"\n2","£\n3","$\n4","%\n5","^\n6","&\n7","*\n8","(\n9",")\n0","_ ⌦\n- ","⌫ =\n +"], +["↹","q σ\nâ ϕ", "w ω\n Ω", "e ε\n ℇ","r ρ\n ∇","t ϑ\nț θ","y ℝ\n ℤ","u ∫\n ℂ","i ∫\nî ∮","o ∞\n ⊗","p π\n ∏","[ \n{","]\n{"], +["~\n⇪ #","a α\nă Α","s ∑\nș ⨋","d δ\n ∂","f φ\n ψ","g γ\n Γ","h ℏ\n 𝓗","j ∈\n ∉","k ϰ\n ∆","l λ\n Λ",": 𝔼\nÅ ;","@ ∝\n' ℒ","⌅"], +["⇧","|\n\\","z ζ\n ∡","x ξ\n Ξ","c χ\nç ̇","v ν\n Ν","b β\n Β","n η\n ∪","m μ\n ∘","< ≊\n, ∓","> ±\n. ∓","↑","? ×\n/ ⋅"], ["⎈","","⇓","⎇","⇑","","","","⎇","⇧","←","↓","→"]] lay= [[1]*13 , [1]*13,[1]*13,[1]*13,[1]*13] fonts=[ - ["DejaVu Sans Mono"]+["Noto Sans"]*11+["DejaVu Sans Mono"], - ["Noto Sans"]*13, - ["Noto Sans"]*13, - ["Noto Sans"]*13, - ["DejaVu Sans Mono"]+["Font Awesome 6 Brands"]+["Noto Sans"]*11 + ["DejaVu Sans Mono"]*13, + ["DejaVu Sans Mono"]*13, + ["DejaVu Sans Mono"]*13, + ["DejaVu Sans Mono"]*13, + ["DejaVu Sans Mono"]+["Font Awesome 6 Brands"]+["DejaVu Sans Mono"]+["DejaVu Sans Symbols"]+["DejaVu Sans Mono"]*4+["DejaVu Sans Symbols"]+["DejaVu Sans Mono"]*4 ] sx=19.05 sy=19.05 @@ -24,7 +24,7 @@ i = -1 j = -1 angles=[9,8.5,-6,-7,0] - +vfs=[0,9,7,6,4.5,4.5] for row,ll,ff in zip(leg,lay,fonts): y -= sy i += 1 @@ -38,6 +38,11 @@ if k == '': convex=True scoop = 2.5 + fs=3 + if len(k)<=5: + fs=vfs[len(k)] + if (len(k.split("\n"))==2): + fs = 4.5 if k in ['f','F','j','J']: scoop = 2.5*1.2 m65.add(keycap(legend=k, @@ -45,6 +50,7 @@ font=f, convex=convex, depth = scoop, + fontsize = fs, unitX=l), name="k{}{}".format(i,j), loc=cq.Location(cq.Vector(x,y,0))) diff --git a/examples/planck.py b/examples/planck.py index 50556a0..ffee957 100644 --- a/examples/planck.py +++ b/examples/planck.py @@ -5,14 +5,14 @@ leg = [ ["⎋", "q","w", "e","r","t","y","u","i","o","p","⌫"], - ["↹","a","s","d","f","g","h","j","k","l",";","'"], - ["⇧","z","x","c","v","b","n","m",",",".","/","⌅"], + ["↹","a","s","d","f","g","h","j","k","l",":\n;","@\n'"], + ["⇧","z","x","c","v","b","n","m","<\n,",">\n.","?\n/","⌅"], ["⎈","⎇","","⎇","⇓","","⇑","←","↓","↑","→"]] lay= [[1]*12, [1]*12,[1]*12,[1]*5+[2]+[1]*5] fonts=[ - ["DejaVu Sans Mono"]+["Noto Sans"]*10+["DejaVu Sans Mono"], - ["Noto Sans"]*12, - ["Noto Sans"]*12, + ["DejaVu Sans Mono"]*12, + ["DejaVu Sans Mono"]*12, + ["DejaVu Sans Mono"]*12, ["DejaVu Sans Mono"]*11 ] sx=19.05 @@ -22,6 +22,7 @@ i = -1 j = -1 angles=[8.5,-6,-7,0] +vfs=[0,9,7,6,4.5,4.5] for row,ll,ff in zip(leg,lay,fonts): y -= sy @@ -38,14 +39,20 @@ scoop = 2.5 if k in ['f','F','j','J']: scoop = 2.5*1.2 + fs=3 + if len(k)<=5: + fs=vfs[len(k)] + if (len(k.split("\n"))==2): + fs = 4.5 planck.add(keycap(legend=k, angle=angles[i], font=f, depth = scoop, convex=convex, + fontsize = fs, unitX=l), name="k{}{}".format(i,j), loc=cq.Location(cq.Vector(x,y,0))) x += w -cq.exporters.export(planck.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) -#show_object(planck, name="pre", options={"alpha": 0}) +#cq.exporters.export(planck.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +show_object(planck, name="pre", options={"alpha": 0}) diff --git a/examples/preonic.py b/examples/preonic.py index ee4ffea..8d0953a 100644 --- a/examples/preonic.py +++ b/examples/preonic.py @@ -4,17 +4,17 @@ from opk import * leg = [ - ["⎋","1","2","3","4","5","6","7","8","9","0","⌫"], - ["q", "w", "e","r","t","y","u","i","o","p","[","]"], - ["↹","a","s","d","f","g","h","j","k","l",";","'"], - ["⇧","z","x","c","v","b","n","m",",",".","/","⌅"], + ["⎋","!\n1","\"\n2","£\n3","$\n4","%\n5","^\n6","&\n7","*\n8","(\n9",")\n0","⌫"], + ["q", "w", "e","r","t","y","u","i","o","p","{\n[","}\n]"], + ["↹","a","s","d","f","g","h","j","k","l",":\n;","@\n'"], + ["⇧","z","x","c","v","b","n","m","<\n,",">\n.","?\n/","⌅"], ["⎈","⎇","","⎇","⇓","","⇑","←","↓","↑","→"]] lay= [[1]*12, [1]*12,[1]*12,[1]*12,[1]*5+[2]+[1]*5] fonts=[ - ["DejaVu Sans Mono"]+["Noto Sans"]*10+["DejaVu Sans Mono"], - ["Noto Sans"]*12, - ["Noto Sans"]*12, - ["Noto Sans"]*12, + ["DejaVu Sans Mono"]*12, + ["DejaVu Sans Mono"]*12, + ["DejaVu Sans Mono"]*12, + ["DejaVu Sans Mono"]*12, ["DejaVu Sans Mono"]*11 ] sx=19.05 @@ -24,6 +24,7 @@ i = -1 j = -1 angles=[9,8.5,-6,-7,0] +vfs=[0,9,7,6,4.5,4.5] for row,ll,ff in zip(leg,lay,fonts): y -= sy @@ -39,15 +40,21 @@ convex=True scoop = 2.5 if k in ['f','F','j','J']: - scoop = 3.5 + scoop = 2.5*1.2 + fs=3 + if len(k)<=5: + fs=vfs[len(k)] + if (len(k.split("\n"))==2): + fs = 4.5 pre.add(keycap(legend=k, angle=angles[i], font=f, depth = scoop, convex=convex, + fontsize =fs, unitX=l), name="k{}{}".format(i,j), loc=cq.Location(cq.Vector(x,y,0))) x += w -cq.exporters.export(pre.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +#cq.exporters.export(pre.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) show_object(pre, name="pre", options={"alpha": 0}) diff --git a/examples/tkl.py b/examples/tkl.py new file mode 100644 index 0000000..ccd5ad2 --- /dev/null +++ b/examples/tkl.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 + +import cadquery as cq +from opk import * +leg = [ ["⎋","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12", "Prn\nScr","Scr\nLock","Pause"], + ["¬\n`","!\n1","\"\n2","£\n3","$\n4","%\n5","^\n6","&\n7","*\n8","(\n9",")\n0","_\n-","=\n+","⌫","Ins","Home","Pg\nUp"], +["↹","q","w","e","r","t","y","u","i","o","p","[","]","|\n\\","Del","End","Pg\nDn"], +["⇪","a","s","d","f","g","h","j","k","l",";","'","enter"], +["⇧","z","x","c","v","b","n","m",",",".","/","⇧","↑"], +["⎈","","Alt","","Alt","Fn","Mnu","Ctrl","←","↓","→"]] +lay= [[1]*16, + [1]*13+[2]+[1]*3, + [1.5]+[1]*12+[1.5]+[1]*3, + [1.75]+[1]*11+[2.25], + [2.25]+[1]*10+[2.75]+[1], + [1.25,1.25,1.25,6.25,1.25,1.25,1.25,1.25,1,1,1]] +fonts=[ + ["DejaVu Sans Mono"]*16, + ["DejaVu Sans Mono"]*17, + ["DejaVu Sans Mono"]*17, + ["DejaVu Sans Mono"]*13, + ["DejaVu Sans Mono"]*13, + ["DejaVu Sans Mono"]+["Font Awesome 6 Brands"]+["Noto Sans"]*6+["DejaVu Sans Mono"]*3 + ] + +offx=[[0,1,0,0,0]+[0.5,0,0,0]+[0.5,0,0,0]+[0.5,0,0], + [0.0]*14+[0.5,0,0], + [0.0]*14+[0.5,0,0], + [0.0]*13, + [0.0]*12+[1.5], + [0.0]*8+[0.5,0,0] + ] +offy=[[0.5]+[0]*15, + [0]*17, + [0]*17, + [0]*13, + [0]*13, + [0]*11 + ] + +sx=19.05 +sy=19.05 + +m65 = cq.Assembly() + +y = 0 +i = -1 +j = -1 + +angles=[13.5,9,8.5,-6,-7,0] +vfs=[0,9,7,6,4.5,4.5] + +for row,ll,ff,ofx,ofy in zip(leg,lay,fonts,offx,offy): + + i += 1 + y = -(i+1)*sy + + x = 0 + for k,l,f,ox,oy in zip(row,ll,ff,ofx,ofy): + print(k,len(k),l,f) + w = l*sx/2.0 + j += 1 + x += w + ox*sx + y += oy*sy + convex=False + if k == '': + convex=True + scoop = 2.5 + if k in ['f','F','j','J']: + scoop = 2.5*1.2 + fs=3 + if len(k)<=5: + fs=vfs[len(k)] + if (len(k.split("\n"))==2): + fs = 4.5 + m65.add(keycap(legend=k, + angle=angles[i], + font=f, + convex=convex, + depth = scoop, + fontsize=fs, + unitX=l), + name="k{}{}".format(i,j), + loc=cq.Location(cq.Vector(x,y,0)) + ) + x += w + +#cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +show_object(m65, name="legend", options={'color': 'red', 'alpha': 0}) diff --git a/pyproject.toml b/pyproject.toml index 21102d5..629d21d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "opk" -version = "0.0.7" +version = "0.0.8" authors = [ { name="Matteo (Matt3o) Spinelli", email="info@matt3o.com" }, ] diff --git a/src/opk.py b/src/opk.py index d6ba5f6..8df47d0 100644 --- a/src/opk.py +++ b/src/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 @@ -55,7 +55,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 +130,7 @@ def keycap( #show_object(tool, options={'alpha': 0.4}) keycap = keycap - tool - + # Top edge fillet keycap = keycap.edges(">Z").fillet(0.5) From e67a4f3df58478017b935d38eccbe4c8aecf4a87 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Wed, 7 Sep 2022 18:01:07 +0100 Subject: [PATCH 28/44] update --- src/opk.py | 114 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 67 insertions(+), 47 deletions(-) diff --git a/src/opk.py b/src/opk.py index 8df47d0..8439722 100644 --- a/src/opk.py +++ b/src/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 @@ -24,22 +24,22 @@ """ import cadquery as cq -from cadquery import exporters def keycap( unitX: float = 1, # keycap size in unit. Standard sizes: 1, 1.25, 1.5, ... unitY: float = 1, base: float = 18.2, # 1-unit size in mm at the base - top: float = 14.2, # 1-unit size in mm at the top, actual hitting area will be slightly bigger - curv: float = 1.3, # Top side curvature. Higher value makes the top rounder (use small increments) + top: float = 13.2, # 1-unit size in mm at the top, actual hitting area will be slightly bigger + curv: float = 1.7, # Top side curvature. Higher value makes the top rounder (use small increments) bFillet: float = 0.5, # Fillet at the base - tFillet: float = 4, # Fillet at the top + tFillet: float = 5, # Fillet at the top height: float = 13, # Height of the keycap before cutting the scoop (final height is lower) angle: float = 7, # Angle of the top surface - depth: float = 2.4, # Scoop depth + 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 ): @@ -55,7 +55,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 = ( @@ -108,20 +108,20 @@ def keycap( else: tool = ( cq.Workplane("YZ").transformed(offset=cq.Vector(0, height, bx/2), rotate=cq.Vector(0, 0, angle)) - .moveTo(-by/2+2.4,0) - .threePointArc((0, min(-0.1, -depth+1)), (by/2-2.4, 0)) + .moveTo(-by/2+2,0) + .threePointArc((0, min(-0.1, -depth+1.5)), (by/2-2, 0)) .lineTo(by/2, height) .lineTo(-by/2, height) .close() .workplane(offset=-bx/2) - .moveTo(-by/2, -1) - .threePointArc((0, -depth), (by/2, -1)) + .moveTo(-by/2-2, -0.5) + .threePointArc((0, -depth), (by/2+2, -0.5)) .lineTo(by/2, height) .lineTo(-by/2, height) .close() .workplane(offset=-bx/2) - .moveTo(-by/2+2.4, 0) - .threePointArc((0, min(-0.1, -depth+1)), (by/2-2.4, 0)) + .moveTo(-by/2+2, 0) + .threePointArc((0, min(-0.1, -depth+1.5)), (by/2-2, 0)) .lineTo(by/2, height) .lineTo(-by/2, height) .close() @@ -130,44 +130,58 @@ def keycap( #show_object(tool, options={'alpha': 0.4}) keycap = keycap - tool - + # Top edge fillet - keycap = keycap.edges(">Z").fillet(0.5) + keycap = keycap.edges(">Z").fillet(0.6) # Since the shell() function is not able to deal with complex shapes # we need to subtract a smaller keycap from the main shape shell = ( cq.Workplane("XY").rect(bx-thickness*2, by-thickness*2) - .workplane(offset=height/5).rect(bx-thickness*2.3, by-thickness*2.3) - .workplane().transformed(offset=cq.Vector(0, 0, height-height/5-4), rotate=cq.Vector(angle, 0, 0)).rect(tx-thickness*2, ty-thickness*2) + .workplane(offset=height/4).rect(bx-thickness*3, by-thickness*3) + .workplane().transformed(offset=cq.Vector(0, 0, height-height/4-4.5), rotate=cq.Vector(angle, 0, 0)).rect(tx-thickness*2, ty-thickness*2) .loft() ) keycap = keycap - shell # Build the stem and the keycap guts - if unitX < 2: - stem_pts = [(0,0)] - elif unitX < 3: - dist = 2.25 / 2 * 19.05 - 19.05 / 2 - stem_pts = [(0,0), (dist, 0), (-dist,0)] + # TODO: we need to find a better way to build the stem... + if ( unitY > unitX ): + if unitY > 1.75: + dist = 2.25 / 2 * 19.05 - 19.05 / 2 + stem_pts = [(0, 0), (0, dist), (0, -dist)] + stem1 = ( + cq.Sketch() + .rect(0.8, ty) + .push(stem_pts) + .rect(tx, 0.8) + .circle(2.75) + .clean() + ) else: - dist = unitX / 2 * 19.05 - 19.05 / 2 - stem_pts = [(0,0), (dist, 0), (-dist,0)] - - stem1 = ( - cq.Sketch() - .rect(tx, 0.8) - .push(stem_pts) - .rect(0.8, ty) - .circle(2.75) - .clean() - ) - + if unitX < 2: + stem_pts = [(0,0)] + elif unitX < 3: # keycaps smaller than 3unit all have 2.25 stabilizers + dist = 2.25 / 2 * 19.05 - 19.05 / 2 + stem_pts = [(0,0), (dist, 0), (-dist,0)] + else: + dist = unitX / 2 * 19.05 - 19.05 / 2 + stem_pts = [(0,0), (dist, 0), (-dist,0)] + + stem1 = ( + cq.Sketch() + .rect(tx, 0.8) + .push(stem_pts) + .rect(0.8, ty) + .circle(2.75) + .clean() + ) + stem2 = ( cq.Sketch() .push(stem_pts) - .rect(4.1, 1.25) - .rect(1.1, 4.1) + .rect(4.15, 1.27) + .rect(1.12, 4.15) .clean() ) @@ -187,7 +201,7 @@ def keycap( ) # Add the legend if present - if legend: + if legend and legendDepth != 0: legend = ( cq.Workplane("XY").transformed(offset=cq.Vector(0, 0, height+1), rotate=cq.Vector(angle, 0, 0)) .text(legend, fontsize, -4, font=font, halign="center", valign="center") @@ -196,10 +210,16 @@ def keycap( # try to center the legend horizontally legend = legend.translate((-bb.center.x, 0, 0)) - legend = legend - keycap - legend = legend.translate((0,0,-1)) - keycap = keycap - legend - legend = legend - tool # this can be used to export the legend for 2 colors 3D printing + 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}) From 65d417c504f9b6a9fb3558a377c720504ff2c52f Mon Sep 17 00:00:00 2001 From: Alin Marin Elena Date: Wed, 7 Sep 2022 21:02:44 +0100 Subject: [PATCH 29/44] update --- examples/keycap.py | 8 ++++---- examples/m65.py | 37 +++++++++++++++++++++++++------------ examples/test.scad | 2 +- pyproject.toml | 2 +- src/opk.py | 37 ++++++++++++++++++++++--------------- 5 files changed, 53 insertions(+), 33 deletions(-) diff --git a/examples/keycap.py b/examples/keycap.py index b90af47..6ebbfc7 100644 --- a/examples/keycap.py +++ b/examples/keycap.py @@ -1,7 +1,7 @@ import opk +import cadquery as cq +cap = opk.keycap(legend="",unitX=1,unitY=1,fontPath="./FontAwesome5Brands-Regular-400.otf") +#show_object(cap, name="keycap", options={"alpha": 0}) -cap = opk.keycap(legend="shift",unitX=1,unitY=2) -show_object(cap, name="keycap", options={"alpha": 0}) - -#exporters.export(cap, 'keycap.stl', tolerance=0.001, angularTolerance=0.05) +cq.exporters.export(cap, 'keycap.stl', tolerance=0.001, angularTolerance=0.05) #exporters.export(cap, 'keycap.step') diff --git a/examples/m65.py b/examples/m65.py index 0493ebc..228a952 100644 --- a/examples/m65.py +++ b/examples/m65.py @@ -3,6 +3,7 @@ import cadquery as cq from opk import * +import os leg = [["⎋ `\n ¬","!\n1","\"\n2","£\n3","$\n4","%\n5","^\n6","&\n7","*\n8","(\n9",")\n0","_ ⌦\n- ","⌫ =\n +"], ["↹","q σ\nâ ϕ", "w ω\n Ω", "e ε\n ℇ","r ρ\n ∇","t ϑ\nț θ","y ℝ\n ℤ","u ∫\n ℂ","i ∫\nî ∮","o ∞\n ⊗","p π\n ∏","[ \n{","]\n{"], @@ -15,7 +16,8 @@ ["DejaVu Sans Mono"]*13, ["DejaVu Sans Mono"]*13, ["DejaVu Sans Mono"]*13, - ["DejaVu Sans Mono"]+["Font Awesome 6 Brands"]+["DejaVu Sans Mono"]+["DejaVu Sans Symbols"]+["DejaVu Sans Mono"]*4+["DejaVu Sans Symbols"]+["DejaVu Sans Mono"]*4 + ["DejaVu Sans Mono"]+["/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf"]+ + ["DejaVu Sans Mono"]+["Noto Sans Symbols"]+["DejaVu Sans Mono"]*4+["Noto Sans Symbols"]+["DejaVu Sans Mono"]*4 ] sx=19.05 sy=19.05 @@ -45,15 +47,26 @@ fs = 4.5 if k in ['f','F','j','J']: scoop = 2.5*1.2 - m65.add(keycap(legend=k, - angle=angles[i], - font=f, - convex=convex, - depth = scoop, - fontsize = fs, - unitX=l), - name="k{}{}".format(i,j), - loc=cq.Location(cq.Vector(x,y,0))) + if os.path.isfile(f): + m65.add(keycap(legend=k, + angle=angles[i], + fontPath=f, + convex=convex, + depth = scoop, + fontsize = fs, + unitX=l), + name="k{}{}".format(i,j), + loc=cq.Location(cq.Vector(x,y,0))) + else: + m65.add(keycap(legend=k, + angle=angles[i], + fontPath=f, + convex=convex, + depth = scoop, + font = f, + unitX=l), + name="k{}{}".format(i,j), + loc=cq.Location(cq.Vector(x,y,0))) x += w -#cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) -show_object(m65, name="m65", options={"alpha": 0}) +cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +#show_object(m65, name="m65", options={"alpha": 0}) diff --git a/examples/test.scad b/examples/test.scad index 4f51465..fb84af5 100644 --- a/examples/test.scad +++ b/examples/test.scad @@ -1 +1 @@ -import("keycaps.stl"); +import("/home/drFaustroll/lavello/OPK_alin/examples/keycap.stl"); diff --git a/pyproject.toml b/pyproject.toml index 629d21d..0ae50eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "opk" -version = "0.0.8" +version = "0.0.11" authors = [ { name="Matteo (Matt3o) Spinelli", email="info@matt3o.com" }, ] diff --git a/src/opk.py b/src/opk.py index 8439722..ceafc73 100644 --- a/src/opk.py +++ b/src/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 @@ -41,7 +41,8 @@ def keycap( 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 + fontsize: float = 10, + fontPath: str = None ): top_diff = base - top @@ -55,7 +56,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 +131,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 +177,7 @@ def keycap( .circle(2.75) .clean() ) - + stem2 = ( cq.Sketch() .push(stem_pts) @@ -202,10 +203,16 @@ def keycap( # Add the legend if present if legend and legendDepth != 0: - legend = ( - cq.Workplane("XY").transformed(offset=cq.Vector(0, 0, height+1), rotate=cq.Vector(angle, 0, 0)) - .text(legend, fontsize, -4, font=font, halign="center", valign="center") - ) + if fontPath is None: + legend = ( + cq.Workplane("XY").transformed(offset=cq.Vector(0, 0, height+1), rotate=cq.Vector(angle, 0, 0)) + .text(legend, fontsize, -4, font=font, halign="center", valign="center") + ) + else: + legend = ( + cq.Workplane("XY").transformed(offset=cq.Vector(0, 0, height+1), rotate=cq.Vector(angle, 0, 0)) + .text(legend, fontsize, -4, fontPath=fontPath, halign="center", valign="center") + ) bb = legend.val().BoundingBox() # try to center the legend horizontally legend = legend.translate((-bb.center.x, 0, 0)) From 5a901760b4093b57ea4374d39229dceffb08f9e8 Mon Sep 17 00:00:00 2001 From: Alin Marin Elena Date: Thu, 8 Sep 2022 11:09:59 +0100 Subject: [PATCH 30/44] update --- examples/rows.py | 57 ++++++++++++++++++++++++++++++---------------- examples/test.scad | 2 +- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/examples/rows.py b/examples/rows.py index 9546e51..1520bc6 100644 --- a/examples/rows.py +++ b/examples/rows.py @@ -3,22 +3,23 @@ import cadquery as cq from opk import * +import os leg = [["⎋"],["⎋"], ["↹"], ["⇪","d","f","g"], ["⇧"], ["⎈"], -[""]] -lay= [[1],[1],[1.5],[1.75]+[1]*3,[2.25],[1.25],[2]] +["",""]] +lay= [[1],[1],[1.5],[1.75]+[1]*3,[2.25],[1.25],[2,1]] fonts=[ - ["DejaVu Sans Mono"], - ["DejaVu Sans Mono"], - ["DejaVu Sans Mono"], - ["DejaVu Sans Mono"]*4, - ["DejaVu Sans Mono"], - ["DejaVu Sans Mono"], - ["DejaVu Sans Mono"] + ["/usr/share/fonts/truetype/NotoSansSymbols-Medium.ttf"], + ["/usr/share/fonts/truetype/NotoSansSymbols-Medium.ttf"], + ["Noto Sans Mono"], + ["Noto Sans Mono"]*4, + ["Noto Sans Mono"], + ["/usr/share/fonts/truetype/NotoSansSymbols-Medium.ttf"], + ["Noto Sans Mono"]+["/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf"] ] sx = 19.05 sy = 19.05 @@ -27,6 +28,7 @@ i = -1 j = -1 angles = [13.5,9,8.5,-6,-7,0,0] +vfs=[0,9,7,6,4.5,4.5] for row,ll,ff in zip(leg,lay,fonts): y -= sy @@ -38,19 +40,36 @@ j += 1 x += w convex = False - if k == '': + if k in ["",""]: convex = True scoop = 2.5 if k in ['f','F','j','J']: scoop = 2.5*1.2 - rows.add(keycap(legend=k, - angle=angles[i], - font=f, - convex=convex, - depth=scoop, - unitX=l), - name="k{}{}".format(i,j), - loc=cq.Location(cq.Vector(x,y,0))) + fs=3 + if len(k)<=5: + fs=vfs[len(k)] + if (len(k.split("\n"))==2): + fs = 4.5 + if os.path.isfile(f): + rows.add(keycap(legend=k, + angle=angles[i], + fontPath=f, + convex=convex, + depth = scoop, + fontsize = fs, + unitX=l), + name="k{}{}".format(i,j), + loc=cq.Location(cq.Vector(x,y,0))) + else: + rows.add(keycap(legend=k, + angle=angles[i], + fontPath=f, + convex=convex, + depth = scoop, + font = f, + unitX=l), + name="k{}{}".format(i,j), + loc=cq.Location(cq.Vector(x,y,0))) x += w cq.exporters.export(rows.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) -show_object(rows, name="rows", options={"alpha": 0}) +#show_object(rows, name="rows", options={"alpha": 0}) diff --git a/examples/test.scad b/examples/test.scad index fb84af5..80beb27 100644 --- a/examples/test.scad +++ b/examples/test.scad @@ -1 +1 @@ -import("/home/drFaustroll/lavello/OPK_alin/examples/keycap.stl"); +import("/home/drFaustroll/lavello/OPK_alin/examples/keycaps.stl"); From d2aded5ed3c400546c7967d0c3b7c29ce1c109ce Mon Sep 17 00:00:00 2001 From: Alin Marin Elena Date: Thu, 8 Sep 2022 23:07:12 +0100 Subject: [PATCH 31/44] update --- src/opk.py | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/opk.py b/src/opk.py index ceafc73..20e67d0 100644 --- a/src/opk.py +++ b/src/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 @@ -41,8 +41,7 @@ def keycap( 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, - fontPath: str = None + fontsize: float = 10 ): top_diff = base - top @@ -56,7 +55,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 = ( @@ -131,7 +130,7 @@ def keycap( #show_object(tool, options={'alpha': 0.4}) keycap = keycap - tool - + # Top edge fillet keycap = keycap.edges(">Z").fillet(0.6) @@ -177,7 +176,7 @@ def keycap( .circle(2.75) .clean() ) - + stem2 = ( cq.Sketch() .push(stem_pts) @@ -203,16 +202,15 @@ def keycap( # Add the legend if present if legend and legendDepth != 0: - if fontPath is None: - legend = ( - cq.Workplane("XY").transformed(offset=cq.Vector(0, 0, height+1), rotate=cq.Vector(angle, 0, 0)) - .text(legend, fontsize, -4, font=font, halign="center", valign="center") - ) - else: - legend = ( - cq.Workplane("XY").transformed(offset=cq.Vector(0, 0, height+1), rotate=cq.Vector(angle, 0, 0)) - .text(legend, fontsize, -4, fontPath=fontPath, halign="center", valign="center") - ) + 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)) From 9d6d84fff29df9b99b2c05bd09383ef3cad913af Mon Sep 17 00:00:00 2001 From: Alin Marin Elena Date: Thu, 8 Sep 2022 23:07:22 +0100 Subject: [PATCH 32/44] update --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0ae50eb..21fc3cd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "opk" -version = "0.0.11" +version = "0.0.12" authors = [ { name="Matteo (Matt3o) Spinelli", email="info@matt3o.com" }, ] From d033aa387e46fc5a9b2de0a3926d74eefa225093 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Thu, 8 Sep 2022 23:15:55 +0100 Subject: [PATCH 33/44] update --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 21fc3cd..9242200 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "opk" -version = "0.0.12" +version = "0.0.13" authors = [ { name="Matteo (Matt3o) Spinelli", email="info@matt3o.com" }, ] From 7f11dd0eb0befa97cdbd3f2582220d88fe24e431 Mon Sep 17 00:00:00 2001 From: Alin Marin Elena Date: Sun, 11 Sep 2022 07:55:35 +0100 Subject: [PATCH 34/44] update --- src/opk.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/opk.py b/src/opk.py index 20e67d0..bf295b9 100644 --- a/src/opk.py +++ b/src/opk.py @@ -88,8 +88,8 @@ def keycap( keycap = ( cq.Workplane("XY") .placeSketch(base, - mid.moved(cq.Location(cq.Vector(0, 0, height/4), (1,0,0), angle/4)), - top.moved(cq.Location(cq.Vector(0, 0, height), (1,0,0), angle)) + mid.moved(cq.Location(cq.Vector(0, 0, height/4), cq.Vector(1,0,0), angle/4)), + top.moved(cq.Location(cq.Vector(0, 0, height), cq.Vector(1,0,0), angle)) ) .loft() ) From 9d999bf45c94186408de7487c86dc5050091a9b8 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Sun, 11 Sep 2022 13:07:05 +0100 Subject: [PATCH 35/44] update --- examples/keycap.py | 10 +++++---- examples/m65.py | 55 ++++++++++++++++++++++------------------------ examples/rows.py | 29 ++++++++---------------- examples/test.scad | 2 +- 4 files changed, 42 insertions(+), 54 deletions(-) diff --git a/examples/keycap.py b/examples/keycap.py index 6ebbfc7..4102c94 100644 --- a/examples/keycap.py +++ b/examples/keycap.py @@ -1,7 +1,9 @@ -import opk +from opk import * import cadquery as cq -cap = opk.keycap(legend="",unitX=1,unitY=1,fontPath="./FontAwesome5Brands-Regular-400.otf") -#show_object(cap, name="keycap", options={"alpha": 0}) -cq.exporters.export(cap, 'keycap.stl', tolerance=0.001, angularTolerance=0.05) +cap = keycap(legend="",unitX=1,unitY=1.0, font="/usr/share/fonts/truetype/Font_Awesome_6_Brands-Regular-400.otf",convex=True) +cq.exporters.export(cap, 'space-penguin.stl', tolerance=0.001, angularTolerance=0.05) +cs = keycap(legend="",unitX=1,unitY=1.0, font="/usr/share/fonts/truetype/Font_Awesome_6_Brands-Regular-400.otf",convex=True) +cq.exporters.export(cs, 'space-cameleon.stl', tolerance=0.001, angularTolerance=0.05) + #exporters.export(cap, 'keycap.step') diff --git a/examples/m65.py b/examples/m65.py index 228a952..098a815 100644 --- a/examples/m65.py +++ b/examples/m65.py @@ -5,10 +5,10 @@ from opk import * import os -leg = [["⎋ `\n ¬","!\n1","\"\n2","£\n3","$\n4","%\n5","^\n6","&\n7","*\n8","(\n9",")\n0","_ ⌦\n- ","⌫ =\n +"], -["↹","q σ\nâ ϕ", "w ω\n Ω", "e ε\n ℇ","r ρ\n ∇","t ϑ\nț θ","y ℝ\n ℤ","u ∫\n ℂ","i ∫\nî ∮","o ∞\n ⊗","p π\n ∏","[ \n{","]\n{"], -["~\n⇪ #","a α\nă Α","s ∑\nș ⨋","d δ\n ∂","f φ\n ψ","g γ\n Γ","h ℏ\n 𝓗","j ∈\n ∉","k ϰ\n ∆","l λ\n Λ",": 𝔼\nÅ ;","@ ∝\n' ℒ","⌅"], -["⇧","|\n\\","z ζ\n ∡","x ξ\n Ξ","c χ\nç ̇","v ν\n Ν","b β\n Β","n η\n ∪","m μ\n ∘","< ≊\n, ∓","> ±\n. ∓","↑","? ×\n/ ⋅"], +leg = [["⎋ `\n ¬","1\n!","2\n\"","3\n£","4\n$","5\n%","6\n^","7\n&","8\n*","9\n(","0\n)","- ⌦\n_ ","⌫ =\n +"], +["↹","q σ\nâ ϕ", "w ω\n Ω", "e ε\n ℇ","r ρ\n ∇","t ϑ\nț θ","y ℝ\n ℤ","u τ\n ℂ","i ∫\nî ∮","o ∞\n ⊗","p π\n ∏","[ \n{","]\n{"], +["#\n~ ⇪","a α\nă Α","s ∑\nș ⨋","d δ\n ∂","f φ\n ψ","g γ\n Γ","h ℏ\n 𝓗","j ∈\n ∉","k ϰ\n ∆","l λ\n Λ","; 𝔼\nÅ :","' ∝\n@ ℒ","⌅"], +["⇧","|\n\\","z ζ\n ∡","x ξ\n Ξ","c χ\nç ̇","v ν\n Ν","b β\n Β","n η\n ∪","m μ\n ∘",", ≊\n< ∓",". ±\n> ∓","↑","? ×\n/ ⋅"], ["⎈","","⇓","⎇","⇑","","","","⎇","⇧","←","↓","→"]] lay= [[1]*13 , [1]*13,[1]*13,[1]*13,[1]*13] fonts=[ @@ -17,7 +17,14 @@ ["DejaVu Sans Mono"]*13, ["DejaVu Sans Mono"]*13, ["DejaVu Sans Mono"]+["/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf"]+ - ["DejaVu Sans Mono"]+["Noto Sans Symbols"]+["DejaVu Sans Mono"]*4+["Noto Sans Symbols"]+["DejaVu Sans Mono"]*4 + ["DejaVu Sans Mono"]+["/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf"]+ + ["DejaVu Sans Mono"]*4+["/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf"]+["DejaVu Sans Mono"]*4 + ] +colour=[["red"]+["yellow"]*11+["red"], + ["red"]+["yellow"]*10+["red"]*2, + ["red"]+["yellow"]*9+["red"]*3, + ["red"]*2+["yellow"]*7+["red"]*2+["green"]+["red"], + ["red"]*5+["white"]*3+["red"]*2+["green"]*3 ] sx=19.05 sy=19.05 @@ -27,11 +34,11 @@ j = -1 angles=[9,8.5,-6,-7,0] vfs=[0,9,7,6,4.5,4.5] -for row,ll,ff in zip(leg,lay,fonts): +for row,ll,ff,cc in zip(leg,lay,fonts,colour): y -= sy i += 1 x = 0 - for k,l,f in zip(row,ll,ff): + for k,l,f,c in zip(row,ll,ff,cc): print(k,l) w = l*sx/2.0 j += 1 @@ -47,26 +54,16 @@ fs = 4.5 if k in ['f','F','j','J']: scoop = 2.5*1.2 - if os.path.isfile(f): - m65.add(keycap(legend=k, - angle=angles[i], - fontPath=f, - convex=convex, - depth = scoop, - fontsize = fs, - unitX=l), - name="k{}{}".format(i,j), - loc=cq.Location(cq.Vector(x,y,0))) - else: - m65.add(keycap(legend=k, - angle=angles[i], - fontPath=f, - convex=convex, - depth = scoop, - font = f, - unitX=l), - name="k{}{}".format(i,j), - loc=cq.Location(cq.Vector(x,y,0))) + m65.add(keycap(legend=k, + angle=angles[i], + font=f, + convex=convex, + depth = scoop, + fontsize = fs, + unitX=l), + name="k{}{}".format(i,j), + loc=cq.Location(cq.Vector(x,y,0)), + color=cq.Color(c)) x += w -cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) -#show_object(m65, name="m65", options={"alpha": 0}) +#cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +show_object(m65, name="m65", options={"alpha": 0}) diff --git a/examples/rows.py b/examples/rows.py index 1520bc6..a0e509c 100644 --- a/examples/rows.py +++ b/examples/rows.py @@ -50,26 +50,15 @@ fs=vfs[len(k)] if (len(k.split("\n"))==2): fs = 4.5 - if os.path.isfile(f): - rows.add(keycap(legend=k, - angle=angles[i], - fontPath=f, - convex=convex, - depth = scoop, - fontsize = fs, - unitX=l), - name="k{}{}".format(i,j), - loc=cq.Location(cq.Vector(x,y,0))) - else: - rows.add(keycap(legend=k, - angle=angles[i], - fontPath=f, - convex=convex, - depth = scoop, - font = f, - unitX=l), - name="k{}{}".format(i,j), - loc=cq.Location(cq.Vector(x,y,0))) + + rows.add(keycap(legend=k, + angle=angles[i], + font=f, + convex=convex, + depth = scoop, + unitX=l), + name="k{}{}".format(i,j), + loc=cq.Location(cq.Vector(x,y,0))) x += w cq.exporters.export(rows.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) #show_object(rows, name="rows", options={"alpha": 0}) diff --git a/examples/test.scad b/examples/test.scad index 80beb27..9967a48 100644 --- a/examples/test.scad +++ b/examples/test.scad @@ -1 +1 @@ -import("/home/drFaustroll/lavello/OPK_alin/examples/keycaps.stl"); +import("k0-1.stl"); From b73723739f394ce31b1a5edece069f1404f648a9 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Sun, 11 Sep 2022 14:22:53 +0100 Subject: [PATCH 36/44] update --- examples/m65-new.py | 150 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 examples/m65-new.py diff --git a/examples/m65-new.py b/examples/m65-new.py new file mode 100644 index 0000000..c44851c --- /dev/null +++ b/examples/m65-new.py @@ -0,0 +1,150 @@ +import opk +import cadquery as cq +from cadquery import exporters +from random import choice + +keys = { + 0: [ + { 'w': 1,'t':'⎋ `\n ¬','fs':5}, + { 'w': 1,'t':'1\n!','fs':5 }, + { 'w': 1,'t':'2\n\"','fs':5 }, + { 'w': 1,'t':'3\n£','fs':5 }, + { 'w': 1,'t':'4\n$','fs':5 }, + { 'w': 1,'t':'5\n%','fs':5 }, + { 'w': 1,'t':'6\n^','fs':5 }, + { 'w': 1,'t':'7\n&','fs':5 }, + { 'w': 1,'t':'8\n*','fs':5 }, + { 'w': 1,'t':'9\n(','fs':5 }, + { 'w': 1,'t':'0\n)','fs':5 }, + { 'w': 1,'t':'- ⌦\n_ ','fs':5 }, + { 'w': 1,'t':'⌫ =\n +','fs':5 } + ], + 1: [ + { 'w': 1,'t':'↹','fs':12}, + { 'w': 1,'t':'q' }, + { 'w': 1,'t':'w' }, + { 'w': 1,'t':'e' }, + { 'w': 1,'t':'r' }, + { 'w': 1,'t':'t' }, + { 'w': 1,'t':'y' }, + { 'w': 1,'t':'u' }, + { 'w': 1,'t':'i' }, + { 'w': 1,'t':'o' }, + { 'w': 1,'t':'p' }, + { 'w': 1,'t':'[\n{','fs':5 }, + { 'w': 1,'t':']\n}','fs':5 } + ], + 2: [ + { 'w': 1,'t':'#\n~','fs':5 }, + { 'w': 1,'t':'a' }, + { 'w': 1,'t':'s' }, + { 'w': 1,'t':'d' }, + { 'w': 1,'t':'f','n': True }, + { 'w': 1,'t':'g' }, + { 'w': 1,'t':'h' }, + { 'w': 1,'t':'j','n': True }, + { 'w': 1,'t':'k' }, + { 'w': 1,'t':'l' }, + { 'w': 1,'t':';\n:','fs':5 }, + { 'w': 1,'t':'\'\n@','fs':5 }, + { 'w': 1,'t':'⊼','fs':12 }, + ], + 3: [ + { 'w': 1,'t':'⇧','fs':12 }, + { 'w': 1,'t':'\\\n|','fs':5 }, + { 'w': 1,'t':'z' }, + { 'w': 1,'t':'x' }, + { 'w': 1,'t':'c' }, + { 'w': 1,'t':'v' }, + { 'w': 1,'t':'b' }, + { 'w': 1,'t':'n' }, + { 'w': 1,'t':'m' }, + { 'w': 1,'t':',\n<','fs':5 }, + { 'w': 1,'t':'.\n>','fs':5 }, + { 'w': 1,'t':'↑','fs':12 }, + { 'w': 1,'t':'/\n?','fs':5 } + ], + 4: [ + { 'w': 1,'t':'⎈','f':"DejaVu Sans Mono",'fs':12 }, + { 'w': 1,'t':'','f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf" }, + { 'w': 1,'t':'⇓','f':"DejaVu Sans Mono",'fs':12}, + { 'w': 1,'t':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, + { 'w': 1,'t':'⇑','f':"DejaVu Sans Mono",'fs':12 }, + {'w':1,'convex':True}, + {'w':1,'t':'','convex':True,'f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf",'fs':9}, + {'w':1,'convex':True}, + { 'w': 1,'t':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, + { 'w': 1,'t':'⇧','fs':12 }, + { 'w': 1,'t':'←','fs':12 }, + { 'w': 1,'t':'↓','fs':12 }, + { 'w': 1,'t':'→','fs':12 } + + ], +} +colours=["tomato2","springgreen3","slateblue2","sienna1","seagreen3","orangered2","orchid2","maroon2","limegreen","lightseagreen","lightcoral","magenta3","yellow"] + +#rows = [ + #{'angle': 13, 'height': 16, 'keys': keys[0] }, # row 0, function row + #{'angle': 9, 'height': 14, 'keys': keys[1] }, # row 1, numbers row + #{'angle': 8, 'height': 12, 'keys': keys[2] }, # row 2, QWERT + #{'angle': -6, 'height': 11.5, 'keys': keys[3] }, # row 3, ASDFG + #{'angle': -8, 'height': 13, 'keys': keys[4] }, # row 4, ZXCVB + #{'angle': 0, 'height': 12.5, 'keys': keys[5] }, # row 5, bottom row +#] +rows = [ + {'angle': 9, 'height': 14, 'keys': keys[0] }, # row 1, numbers row + {'angle': 8, 'height': 12, 'keys': keys[1] }, # row 2, QWERT + {'angle': -6, 'height': 11.5, 'keys': keys[2] }, # row 3, ASDFG + {'angle': -8, 'height': 13, 'keys': keys[3] }, # row 4, ZXCVB + {'angle': 0, 'height': 12.5, 'keys': keys[4] }, # row 5, bottom row +] + +mainFont = "DejaVu Sans Mono" +mainSize = 9 +assy = cq.Assembly() + +y = 0 +for i, r in enumerate(rows): + x = 0 + for j,k in enumerate(r['keys']): + name = "row{}_{}_U{}".format(i,j,k['w']) + convex = False + if 'convex' in k: + convex = k['convex'] + name+= "_space" + + depth = 2.8 + if 'n' in k: + if k['n']: + name+= "_homing" + depth = 3.6 + legend = '' + if 't' in k: + legend = k['t'] + font = mainFont + if 'f' in k: + font = k['f'] + fontSize=mainSize + if 'fs' in k: + fontSize = k['fs'] + print("Generating: {} {}".format(name, legend)) + cap = opk.keycap(angle=r['angle'], height=r['height'], unitX=k['w'], + convex=convex, depth=depth, + legend=legend, font=font, + fontsize=fontSize) + # Export one key at the time + #exporters.export(cap, './export/STEP/' + name + '.step') + #exporters.export(cap, './export/STL/' + name + '.stl', tolerance=0.001, angularTolerance=0.05) + w = 19.05 * k['w'] / 2 + x+= w + assy.add(cap, name=name, color=cq.Color(choice(colours)), + loc=cq.Location(cq.Vector(x,y,0))) + x+= w + y -= 19.05 + +if 'show_object' in locals(): + show_object(assy) + +# Export the whole assembly, very handy especially for STEP +#exporters.export(assy.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +#exporters.export(assy.toCompound(), 'keycaps.step') From d60edc251138f8867337ec0941e4134533a92862 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Sun, 11 Sep 2022 15:07:35 +0100 Subject: [PATCH 37/44] update --- examples/numpad.py | 113 +++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- 2 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 examples/numpad.py diff --git a/examples/numpad.py b/examples/numpad.py new file mode 100644 index 0000000..5aef720 --- /dev/null +++ b/examples/numpad.py @@ -0,0 +1,113 @@ +import opk +import cadquery as cq +from cadquery import exporters +from random import choice + +keys = { + 0: [ + { 't':'Num','fs':7}, + { 't':'/'}, + { 't':'*' }, + { 't':'-'} + ], + 1: [ + { 't':'7' }, + { 't':'8' }, + { 't':'9' }, + { 't':'+','h':2,'oy':-0.5 } + ], + 2: [ + { 't':'4' }, + { 't':'5','n':True }, + { 't':'6' } + ], + 3: [ + { 't':'1' }, + { 't':'2' }, + { 't':'3' }, + { 't':'⊼','h':2,'oy':-0.5 }, + ], + 4: [ + { 'w': 2,'t':'0' }, + { 't':'.' } + ], +} +colours=["tomato2","springgreen3","slateblue2","sienna1","seagreen3","orangered2","orchid2","maroon2","limegreen","lightseagreen","lightcoral","magenta3","yellow"] + +#rows = [ + #{'angle': 13, 'height': 16, 'keys': keys[0] }, # row 0, function row + #{'angle': 9, 'height': 14, 'keys': keys[1] }, # row 1, numbers row + #{'angle': 8, 'height': 12, 'keys': keys[2] }, # row 2, QWERT + #{'angle': -6, 'height': 11.5, 'keys': keys[3] }, # row 3, ASDFG + #{'angle': -8, 'height': 13, 'keys': keys[4] }, # row 4, ZXCVB + #{'angle': 0, 'height': 12.5, 'keys': keys[5] }, # row 5, bottom row +#] +rows = [ + {'angle': 9, 'height': 14, 'keys': keys[0] }, # row 1, numbers row + {'angle': 8, 'height': 12, 'keys': keys[1] }, # row 2, QWERT + {'angle': -6, 'height': 11.5, 'keys': keys[2] }, # row 3, ASDFG + {'angle': -8, 'height': 13, 'keys': keys[3] }, # row 4, ZXCVB + {'angle': 0, 'height': 12.5, 'keys': keys[4] }, # row 5, bottom row +] + +mainFont = "DejaVu Sans Mono" +mainSize = 9 +assy = cq.Assembly() + +y = 0 +for i, r in enumerate(rows): + x = 0 + for j,k in enumerate(r['keys']): + kh = 1 + if 'h' in k: + kh = k['h'] + kw = 1 + if 'w' in k: + kw = k['w'] + name = "row{}_{}_U{}".format(i,j,kw) + convex = False + if 'convex' in k: + convex = k['convex'] + name+= "_space" + + depth = 2.8 + if 'n' in k: + if k['n']: + name+= "_homing" + depth = 3.6 + legend = '' + if 't' in k: + legend = k['t'] + font = mainFont + if 'f' in k: + font = k['f'] + fontSize=mainSize + if 'fs' in k: + fontSize = k['fs'] + + print("Generating: {} {}".format(name, legend)) + cap = opk.keycap(angle=r['angle'], height=r['height'], + unitX=kw, unitY=kh, + convex=convex, depth=depth, + legend=legend, font=font, + fontsize=fontSize) + # Export one key at the time + #exporters.export(cap, './export/STEP/' + name + '.step') + #exporters.export(cap, './export/STL/' + name + '.stl', tolerance=0.001, angularTolerance=0.05) + w = 19.05 * kw / 2 + x+= w + oy = 0.0 + if 'oy' in k: + oy = k['oy'] + y += oy*19.05 + assy.add(cap, name=name, color=cq.Color(choice(colours)), + loc=cq.Location(cq.Vector(x,y,0))) + x+= w + y = -(i+1)*19.05 + +if 'show_object' in locals(): + show_object(assy) + +# Export the whole assembly, very handy especially for STEP +#exporters.export(assy.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +#exporters.export(assy.toCompound(), 'keycaps.step') diff --git a/pyproject.toml b/pyproject.toml index 9242200..06cdd37 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "opk" -version = "0.0.13" +version = "0.0.14" authors = [ { name="Matteo (Matt3o) Spinelli", email="info@matt3o.com" }, ] From 2b6d510fb844c3a62bf3140aaf9a2157d43f908a Mon Sep 17 00:00:00 2001 From: alin m elena Date: Sun, 11 Sep 2022 15:07:55 +0100 Subject: [PATCH 38/44] update --- examples/m65-new.py | 150 ------------------------------- examples/m65.py | 209 ++++++++++++++++++++++++++++++-------------- 2 files changed, 145 insertions(+), 214 deletions(-) delete mode 100644 examples/m65-new.py diff --git a/examples/m65-new.py b/examples/m65-new.py deleted file mode 100644 index c44851c..0000000 --- a/examples/m65-new.py +++ /dev/null @@ -1,150 +0,0 @@ -import opk -import cadquery as cq -from cadquery import exporters -from random import choice - -keys = { - 0: [ - { 'w': 1,'t':'⎋ `\n ¬','fs':5}, - { 'w': 1,'t':'1\n!','fs':5 }, - { 'w': 1,'t':'2\n\"','fs':5 }, - { 'w': 1,'t':'3\n£','fs':5 }, - { 'w': 1,'t':'4\n$','fs':5 }, - { 'w': 1,'t':'5\n%','fs':5 }, - { 'w': 1,'t':'6\n^','fs':5 }, - { 'w': 1,'t':'7\n&','fs':5 }, - { 'w': 1,'t':'8\n*','fs':5 }, - { 'w': 1,'t':'9\n(','fs':5 }, - { 'w': 1,'t':'0\n)','fs':5 }, - { 'w': 1,'t':'- ⌦\n_ ','fs':5 }, - { 'w': 1,'t':'⌫ =\n +','fs':5 } - ], - 1: [ - { 'w': 1,'t':'↹','fs':12}, - { 'w': 1,'t':'q' }, - { 'w': 1,'t':'w' }, - { 'w': 1,'t':'e' }, - { 'w': 1,'t':'r' }, - { 'w': 1,'t':'t' }, - { 'w': 1,'t':'y' }, - { 'w': 1,'t':'u' }, - { 'w': 1,'t':'i' }, - { 'w': 1,'t':'o' }, - { 'w': 1,'t':'p' }, - { 'w': 1,'t':'[\n{','fs':5 }, - { 'w': 1,'t':']\n}','fs':5 } - ], - 2: [ - { 'w': 1,'t':'#\n~','fs':5 }, - { 'w': 1,'t':'a' }, - { 'w': 1,'t':'s' }, - { 'w': 1,'t':'d' }, - { 'w': 1,'t':'f','n': True }, - { 'w': 1,'t':'g' }, - { 'w': 1,'t':'h' }, - { 'w': 1,'t':'j','n': True }, - { 'w': 1,'t':'k' }, - { 'w': 1,'t':'l' }, - { 'w': 1,'t':';\n:','fs':5 }, - { 'w': 1,'t':'\'\n@','fs':5 }, - { 'w': 1,'t':'⊼','fs':12 }, - ], - 3: [ - { 'w': 1,'t':'⇧','fs':12 }, - { 'w': 1,'t':'\\\n|','fs':5 }, - { 'w': 1,'t':'z' }, - { 'w': 1,'t':'x' }, - { 'w': 1,'t':'c' }, - { 'w': 1,'t':'v' }, - { 'w': 1,'t':'b' }, - { 'w': 1,'t':'n' }, - { 'w': 1,'t':'m' }, - { 'w': 1,'t':',\n<','fs':5 }, - { 'w': 1,'t':'.\n>','fs':5 }, - { 'w': 1,'t':'↑','fs':12 }, - { 'w': 1,'t':'/\n?','fs':5 } - ], - 4: [ - { 'w': 1,'t':'⎈','f':"DejaVu Sans Mono",'fs':12 }, - { 'w': 1,'t':'','f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf" }, - { 'w': 1,'t':'⇓','f':"DejaVu Sans Mono",'fs':12}, - { 'w': 1,'t':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, - { 'w': 1,'t':'⇑','f':"DejaVu Sans Mono",'fs':12 }, - {'w':1,'convex':True}, - {'w':1,'t':'','convex':True,'f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf",'fs':9}, - {'w':1,'convex':True}, - { 'w': 1,'t':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, - { 'w': 1,'t':'⇧','fs':12 }, - { 'w': 1,'t':'←','fs':12 }, - { 'w': 1,'t':'↓','fs':12 }, - { 'w': 1,'t':'→','fs':12 } - - ], -} -colours=["tomato2","springgreen3","slateblue2","sienna1","seagreen3","orangered2","orchid2","maroon2","limegreen","lightseagreen","lightcoral","magenta3","yellow"] - -#rows = [ - #{'angle': 13, 'height': 16, 'keys': keys[0] }, # row 0, function row - #{'angle': 9, 'height': 14, 'keys': keys[1] }, # row 1, numbers row - #{'angle': 8, 'height': 12, 'keys': keys[2] }, # row 2, QWERT - #{'angle': -6, 'height': 11.5, 'keys': keys[3] }, # row 3, ASDFG - #{'angle': -8, 'height': 13, 'keys': keys[4] }, # row 4, ZXCVB - #{'angle': 0, 'height': 12.5, 'keys': keys[5] }, # row 5, bottom row -#] -rows = [ - {'angle': 9, 'height': 14, 'keys': keys[0] }, # row 1, numbers row - {'angle': 8, 'height': 12, 'keys': keys[1] }, # row 2, QWERT - {'angle': -6, 'height': 11.5, 'keys': keys[2] }, # row 3, ASDFG - {'angle': -8, 'height': 13, 'keys': keys[3] }, # row 4, ZXCVB - {'angle': 0, 'height': 12.5, 'keys': keys[4] }, # row 5, bottom row -] - -mainFont = "DejaVu Sans Mono" -mainSize = 9 -assy = cq.Assembly() - -y = 0 -for i, r in enumerate(rows): - x = 0 - for j,k in enumerate(r['keys']): - name = "row{}_{}_U{}".format(i,j,k['w']) - convex = False - if 'convex' in k: - convex = k['convex'] - name+= "_space" - - depth = 2.8 - if 'n' in k: - if k['n']: - name+= "_homing" - depth = 3.6 - legend = '' - if 't' in k: - legend = k['t'] - font = mainFont - if 'f' in k: - font = k['f'] - fontSize=mainSize - if 'fs' in k: - fontSize = k['fs'] - print("Generating: {} {}".format(name, legend)) - cap = opk.keycap(angle=r['angle'], height=r['height'], unitX=k['w'], - convex=convex, depth=depth, - legend=legend, font=font, - fontsize=fontSize) - # Export one key at the time - #exporters.export(cap, './export/STEP/' + name + '.step') - #exporters.export(cap, './export/STL/' + name + '.stl', tolerance=0.001, angularTolerance=0.05) - w = 19.05 * k['w'] / 2 - x+= w - assy.add(cap, name=name, color=cq.Color(choice(colours)), - loc=cq.Location(cq.Vector(x,y,0))) - x+= w - y -= 19.05 - -if 'show_object' in locals(): - show_object(assy) - -# Export the whole assembly, very handy especially for STEP -#exporters.export(assy.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) -#exporters.export(assy.toCompound(), 'keycaps.step') diff --git a/examples/m65.py b/examples/m65.py index 098a815..c44851c 100644 --- a/examples/m65.py +++ b/examples/m65.py @@ -1,69 +1,150 @@ -#!/usr/bin/env python3 +import opk +import cadquery as cq +from cadquery import exporters +from random import choice +keys = { + 0: [ + { 'w': 1,'t':'⎋ `\n ¬','fs':5}, + { 'w': 1,'t':'1\n!','fs':5 }, + { 'w': 1,'t':'2\n\"','fs':5 }, + { 'w': 1,'t':'3\n£','fs':5 }, + { 'w': 1,'t':'4\n$','fs':5 }, + { 'w': 1,'t':'5\n%','fs':5 }, + { 'w': 1,'t':'6\n^','fs':5 }, + { 'w': 1,'t':'7\n&','fs':5 }, + { 'w': 1,'t':'8\n*','fs':5 }, + { 'w': 1,'t':'9\n(','fs':5 }, + { 'w': 1,'t':'0\n)','fs':5 }, + { 'w': 1,'t':'- ⌦\n_ ','fs':5 }, + { 'w': 1,'t':'⌫ =\n +','fs':5 } + ], + 1: [ + { 'w': 1,'t':'↹','fs':12}, + { 'w': 1,'t':'q' }, + { 'w': 1,'t':'w' }, + { 'w': 1,'t':'e' }, + { 'w': 1,'t':'r' }, + { 'w': 1,'t':'t' }, + { 'w': 1,'t':'y' }, + { 'w': 1,'t':'u' }, + { 'w': 1,'t':'i' }, + { 'w': 1,'t':'o' }, + { 'w': 1,'t':'p' }, + { 'w': 1,'t':'[\n{','fs':5 }, + { 'w': 1,'t':']\n}','fs':5 } + ], + 2: [ + { 'w': 1,'t':'#\n~','fs':5 }, + { 'w': 1,'t':'a' }, + { 'w': 1,'t':'s' }, + { 'w': 1,'t':'d' }, + { 'w': 1,'t':'f','n': True }, + { 'w': 1,'t':'g' }, + { 'w': 1,'t':'h' }, + { 'w': 1,'t':'j','n': True }, + { 'w': 1,'t':'k' }, + { 'w': 1,'t':'l' }, + { 'w': 1,'t':';\n:','fs':5 }, + { 'w': 1,'t':'\'\n@','fs':5 }, + { 'w': 1,'t':'⊼','fs':12 }, + ], + 3: [ + { 'w': 1,'t':'⇧','fs':12 }, + { 'w': 1,'t':'\\\n|','fs':5 }, + { 'w': 1,'t':'z' }, + { 'w': 1,'t':'x' }, + { 'w': 1,'t':'c' }, + { 'w': 1,'t':'v' }, + { 'w': 1,'t':'b' }, + { 'w': 1,'t':'n' }, + { 'w': 1,'t':'m' }, + { 'w': 1,'t':',\n<','fs':5 }, + { 'w': 1,'t':'.\n>','fs':5 }, + { 'w': 1,'t':'↑','fs':12 }, + { 'w': 1,'t':'/\n?','fs':5 } + ], + 4: [ + { 'w': 1,'t':'⎈','f':"DejaVu Sans Mono",'fs':12 }, + { 'w': 1,'t':'','f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf" }, + { 'w': 1,'t':'⇓','f':"DejaVu Sans Mono",'fs':12}, + { 'w': 1,'t':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, + { 'w': 1,'t':'⇑','f':"DejaVu Sans Mono",'fs':12 }, + {'w':1,'convex':True}, + {'w':1,'t':'','convex':True,'f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf",'fs':9}, + {'w':1,'convex':True}, + { 'w': 1,'t':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, + { 'w': 1,'t':'⇧','fs':12 }, + { 'w': 1,'t':'←','fs':12 }, + { 'w': 1,'t':'↓','fs':12 }, + { 'w': 1,'t':'→','fs':12 } -import cadquery as cq -from opk import * -import os + ], +} +colours=["tomato2","springgreen3","slateblue2","sienna1","seagreen3","orangered2","orchid2","maroon2","limegreen","lightseagreen","lightcoral","magenta3","yellow"] + +#rows = [ + #{'angle': 13, 'height': 16, 'keys': keys[0] }, # row 0, function row + #{'angle': 9, 'height': 14, 'keys': keys[1] }, # row 1, numbers row + #{'angle': 8, 'height': 12, 'keys': keys[2] }, # row 2, QWERT + #{'angle': -6, 'height': 11.5, 'keys': keys[3] }, # row 3, ASDFG + #{'angle': -8, 'height': 13, 'keys': keys[4] }, # row 4, ZXCVB + #{'angle': 0, 'height': 12.5, 'keys': keys[5] }, # row 5, bottom row +#] +rows = [ + {'angle': 9, 'height': 14, 'keys': keys[0] }, # row 1, numbers row + {'angle': 8, 'height': 12, 'keys': keys[1] }, # row 2, QWERT + {'angle': -6, 'height': 11.5, 'keys': keys[2] }, # row 3, ASDFG + {'angle': -8, 'height': 13, 'keys': keys[3] }, # row 4, ZXCVB + {'angle': 0, 'height': 12.5, 'keys': keys[4] }, # row 5, bottom row +] + +mainFont = "DejaVu Sans Mono" +mainSize = 9 +assy = cq.Assembly() -leg = [["⎋ `\n ¬","1\n!","2\n\"","3\n£","4\n$","5\n%","6\n^","7\n&","8\n*","9\n(","0\n)","- ⌦\n_ ","⌫ =\n +"], -["↹","q σ\nâ ϕ", "w ω\n Ω", "e ε\n ℇ","r ρ\n ∇","t ϑ\nț θ","y ℝ\n ℤ","u τ\n ℂ","i ∫\nî ∮","o ∞\n ⊗","p π\n ∏","[ \n{","]\n{"], -["#\n~ ⇪","a α\nă Α","s ∑\nș ⨋","d δ\n ∂","f φ\n ψ","g γ\n Γ","h ℏ\n 𝓗","j ∈\n ∉","k ϰ\n ∆","l λ\n Λ","; 𝔼\nÅ :","' ∝\n@ ℒ","⌅"], -["⇧","|\n\\","z ζ\n ∡","x ξ\n Ξ","c χ\nç ̇","v ν\n Ν","b β\n Β","n η\n ∪","m μ\n ∘",", ≊\n< ∓",". ±\n> ∓","↑","? ×\n/ ⋅"], -["⎈","","⇓","⎇","⇑","","","","⎇","⇧","←","↓","→"]] -lay= [[1]*13 , [1]*13,[1]*13,[1]*13,[1]*13] -fonts=[ - ["DejaVu Sans Mono"]*13, - ["DejaVu Sans Mono"]*13, - ["DejaVu Sans Mono"]*13, - ["DejaVu Sans Mono"]*13, - ["DejaVu Sans Mono"]+["/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf"]+ - ["DejaVu Sans Mono"]+["/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf"]+ - ["DejaVu Sans Mono"]*4+["/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf"]+["DejaVu Sans Mono"]*4 - ] -colour=[["red"]+["yellow"]*11+["red"], - ["red"]+["yellow"]*10+["red"]*2, - ["red"]+["yellow"]*9+["red"]*3, - ["red"]*2+["yellow"]*7+["red"]*2+["green"]+["red"], - ["red"]*5+["white"]*3+["red"]*2+["green"]*3 - ] -sx=19.05 -sy=19.05 -m65 = cq.Assembly() y = 0 -i = -1 -j = -1 -angles=[9,8.5,-6,-7,0] -vfs=[0,9,7,6,4.5,4.5] -for row,ll,ff,cc in zip(leg,lay,fonts,colour): - y -= sy - i += 1 +for i, r in enumerate(rows): x = 0 - for k,l,f,c in zip(row,ll,ff,cc): - print(k,l) - w = l*sx/2.0 - j += 1 - x += w - convex=False - if k == '': - convex=True - scoop = 2.5 - fs=3 - if len(k)<=5: - fs=vfs[len(k)] - if (len(k.split("\n"))==2): - fs = 4.5 - if k in ['f','F','j','J']: - scoop = 2.5*1.2 - m65.add(keycap(legend=k, - angle=angles[i], - font=f, - convex=convex, - depth = scoop, - fontsize = fs, - unitX=l), - name="k{}{}".format(i,j), - loc=cq.Location(cq.Vector(x,y,0)), - color=cq.Color(c)) - x += w -#cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) -show_object(m65, name="m65", options={"alpha": 0}) + for j,k in enumerate(r['keys']): + name = "row{}_{}_U{}".format(i,j,k['w']) + convex = False + if 'convex' in k: + convex = k['convex'] + name+= "_space" + + depth = 2.8 + if 'n' in k: + if k['n']: + name+= "_homing" + depth = 3.6 + legend = '' + if 't' in k: + legend = k['t'] + font = mainFont + if 'f' in k: + font = k['f'] + fontSize=mainSize + if 'fs' in k: + fontSize = k['fs'] + print("Generating: {} {}".format(name, legend)) + cap = opk.keycap(angle=r['angle'], height=r['height'], unitX=k['w'], + convex=convex, depth=depth, + legend=legend, font=font, + fontsize=fontSize) + # Export one key at the time + #exporters.export(cap, './export/STEP/' + name + '.step') + #exporters.export(cap, './export/STL/' + name + '.stl', tolerance=0.001, angularTolerance=0.05) + w = 19.05 * k['w'] / 2 + x+= w + assy.add(cap, name=name, color=cq.Color(choice(colours)), + loc=cq.Location(cq.Vector(x,y,0))) + x+= w + y -= 19.05 + +if 'show_object' in locals(): + show_object(assy) + +# Export the whole assembly, very handy especially for STEP +#exporters.export(assy.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +#exporters.export(assy.toCompound(), 'keycaps.step') From a9c05186ffefbd1eac450c5d74c3804351265156 Mon Sep 17 00:00:00 2001 From: alin m elena Date: Sun, 11 Sep 2022 16:32:40 +0100 Subject: [PATCH 39/44] update --- examples/kb_render.py | 62 +++++++++++++++ examples/m65.py | 176 +++++++++++++++++------------------------- examples/numpad.py | 56 +------------- examples/planck.py | 149 +++++++++++++++++++++-------------- 4 files changed, 228 insertions(+), 215 deletions(-) create mode 100644 examples/kb_render.py diff --git a/examples/kb_render.py b/examples/kb_render.py new file mode 100644 index 0000000..ac76b89 --- /dev/null +++ b/examples/kb_render.py @@ -0,0 +1,62 @@ +import cadquery as cq +from random import choice +import opk + +def render_kb(rows, mainFont="DejaVu Sans Mono", mainSize = 9, sx = 19.05, sy = 19.05, depth = 2.8): + + assy = cq.Assembly() + colours=["tomato2","springgreen3","slateblue2","sienna1","seagreen3","orangered2","orchid2","maroon2","limegreen","lightseagreen","lightcoral","magenta3","yellow"] + + y = 0 + for i, r in enumerate(rows): + x = 0 + for j,k in enumerate(r['keys']): + kh = 1 + if 'h' in k: + kh = k['h'] + kw = 1 + if 'w' in k: + kw = k['w'] + name = "row{}_{}_U{}".format(i,j,kw) + convex = False + if 'convex' in k: + convex = k['convex'] + name+= "_space" + + cdepth = depth + if 'n' in k: + if k['n']: + name+= "_homing" + cdepth = depth + 0.8 + legend = '' + if 't' in k: + legend = k['t'] + font = mainFont + if 'f' in k: + font = k['f'] + fontSize=mainSize + if 'fs' in k: + fontSize = k['fs'] + print("Generating: {} {}".format(name, legend)) + cap = opk.keycap(angle=r['angle'], height=r['height'], + unitX=kw, unitY=kh, + convex=convex, depth=cdepth, + legend=legend, font=font, + fontsize=fontSize) + # Export one key at the time + #exporters.export(cap, './export/STEP/' + name + '.step') + #exporters.export(cap, './export/STL/' + name + '.stl', tolerance=0.001, angularTolerance=0.05) + w = sx * kw / 2 + ox = 0.0 + if 'ox' in k: + ox = k['ox'] + x += w + ox*sx + oy = 0.0 + if 'oy' in k: + oy = k['oy'] + y += oy*sy + assy.add(cap, name=name, color=cq.Color(choice(colours)), + loc=cq.Location(cq.Vector(x,y,0))) + x += w + y = -(i+1)*sy + return assy diff --git a/examples/m65.py b/examples/m65.py index c44851c..e20da30 100644 --- a/examples/m65.py +++ b/examples/m65.py @@ -1,87 +1,86 @@ import opk import cadquery as cq from cadquery import exporters -from random import choice +from kb_render import * keys = { 0: [ - { 'w': 1,'t':'⎋ `\n ¬','fs':5}, - { 'w': 1,'t':'1\n!','fs':5 }, - { 'w': 1,'t':'2\n\"','fs':5 }, - { 'w': 1,'t':'3\n£','fs':5 }, - { 'w': 1,'t':'4\n$','fs':5 }, - { 'w': 1,'t':'5\n%','fs':5 }, - { 'w': 1,'t':'6\n^','fs':5 }, - { 'w': 1,'t':'7\n&','fs':5 }, - { 'w': 1,'t':'8\n*','fs':5 }, - { 'w': 1,'t':'9\n(','fs':5 }, - { 'w': 1,'t':'0\n)','fs':5 }, - { 'w': 1,'t':'- ⌦\n_ ','fs':5 }, - { 'w': 1,'t':'⌫ =\n +','fs':5 } + { 't':'⎋ `\n ¬','fs':5}, + { 't':'1\n!','fs':5 }, + { 't':'2\n\"','fs':5 }, + { 't':'3\n£','fs':5 }, + { 't':'4\n$','fs':5 }, + { 't':'5\n%','fs':5 }, + { 't':'6\n^','fs':5 }, + { 't':'7\n&','fs':5 }, + { 't':'8\n*','fs':5 }, + { 't':'9\n(','fs':5 }, + { 't':'0\n)','fs':5 }, + { 't':'- ⌦\n_ ','fs':5 }, + { 't':'⌫ =\n +','fs':5 } ], 1: [ - { 'w': 1,'t':'↹','fs':12}, - { 'w': 1,'t':'q' }, - { 'w': 1,'t':'w' }, - { 'w': 1,'t':'e' }, - { 'w': 1,'t':'r' }, - { 'w': 1,'t':'t' }, - { 'w': 1,'t':'y' }, - { 'w': 1,'t':'u' }, - { 'w': 1,'t':'i' }, - { 'w': 1,'t':'o' }, - { 'w': 1,'t':'p' }, - { 'w': 1,'t':'[\n{','fs':5 }, - { 'w': 1,'t':']\n}','fs':5 } + { 't':'↹','fs':12}, + { 't':'q' }, + { 't':'w' }, + { 't':'e' }, + { 't':'r' }, + { 't':'t' }, + { 't':'y' }, + { 't':'u' }, + { 't':'i' }, + { 't':'o' }, + { 't':'p' }, + { 't':'[\n{','fs':5 }, + { 't':']\n}','fs':5 } ], 2: [ - { 'w': 1,'t':'#\n~','fs':5 }, - { 'w': 1,'t':'a' }, - { 'w': 1,'t':'s' }, - { 'w': 1,'t':'d' }, - { 'w': 1,'t':'f','n': True }, - { 'w': 1,'t':'g' }, - { 'w': 1,'t':'h' }, - { 'w': 1,'t':'j','n': True }, - { 'w': 1,'t':'k' }, - { 'w': 1,'t':'l' }, - { 'w': 1,'t':';\n:','fs':5 }, - { 'w': 1,'t':'\'\n@','fs':5 }, - { 'w': 1,'t':'⊼','fs':12 }, + { 't':'#\n~','fs':5 }, + { 't':'a' }, + { 't':'s' }, + { 't':'d' }, + { 't':'f','n': True }, + { 't':'g' }, + { 't':'h' }, + { 't':'j','n': True }, + { 't':'k' }, + { 't':'l' }, + { 't':';\n:','fs':5 }, + { 't':'\'\n@','fs':5 }, + { 't':'⊼','fs':12 }, ], 3: [ - { 'w': 1,'t':'⇧','fs':12 }, - { 'w': 1,'t':'\\\n|','fs':5 }, - { 'w': 1,'t':'z' }, - { 'w': 1,'t':'x' }, - { 'w': 1,'t':'c' }, - { 'w': 1,'t':'v' }, - { 'w': 1,'t':'b' }, - { 'w': 1,'t':'n' }, - { 'w': 1,'t':'m' }, - { 'w': 1,'t':',\n<','fs':5 }, - { 'w': 1,'t':'.\n>','fs':5 }, - { 'w': 1,'t':'↑','fs':12 }, - { 'w': 1,'t':'/\n?','fs':5 } + { 't':'⇧','fs':12 }, + { 't':'\\\n|','fs':5 }, + { 't':'z' }, + { 't':'x' }, + { 't':'c' }, + { 't':'v' }, + { 't':'b' }, + { 't':'n' }, + { 't':'m' }, + { 't':',\n<','fs':5 }, + { 't':'.\n>','fs':5 }, + { 't':'↑','fs':12 }, + { 't':'/\n?','fs':5 } ], 4: [ - { 'w': 1,'t':'⎈','f':"DejaVu Sans Mono",'fs':12 }, - { 'w': 1,'t':'','f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf" }, - { 'w': 1,'t':'⇓','f':"DejaVu Sans Mono",'fs':12}, - { 'w': 1,'t':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, - { 'w': 1,'t':'⇑','f':"DejaVu Sans Mono",'fs':12 }, - {'w':1,'convex':True}, - {'w':1,'t':'','convex':True,'f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf",'fs':9}, - {'w':1,'convex':True}, - { 'w': 1,'t':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, - { 'w': 1,'t':'⇧','fs':12 }, - { 'w': 1,'t':'←','fs':12 }, - { 'w': 1,'t':'↓','fs':12 }, - { 'w': 1,'t':'→','fs':12 } + { 't':'⎈','f':"DejaVu Sans Mono",'fs':12 }, + { 't':'','f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf" }, + { 't':'⇓','f':"DejaVu Sans Mono",'fs':12}, + { 't':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, + { 't':'⇑','f':"DejaVu Sans Mono",'fs':12 }, + { 'convex':True}, + { 't':'','convex':True,'f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf",'fs':9}, + { 'convex':True}, + { 't':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, + { 't':'⇧','fs':12 }, + { 't':'←','fs':12 }, + { 't':'↓','fs':12 }, + { 't':'→','fs':12 } ], } -colours=["tomato2","springgreen3","slateblue2","sienna1","seagreen3","orangered2","orchid2","maroon2","limegreen","lightseagreen","lightcoral","magenta3","yellow"] #rows = [ #{'angle': 13, 'height': 16, 'keys': keys[0] }, # row 0, function row @@ -101,46 +100,11 @@ mainFont = "DejaVu Sans Mono" mainSize = 9 -assy = cq.Assembly() -y = 0 -for i, r in enumerate(rows): - x = 0 - for j,k in enumerate(r['keys']): - name = "row{}_{}_U{}".format(i,j,k['w']) - convex = False - if 'convex' in k: - convex = k['convex'] - name+= "_space" - - depth = 2.8 - if 'n' in k: - if k['n']: - name+= "_homing" - depth = 3.6 - legend = '' - if 't' in k: - legend = k['t'] - font = mainFont - if 'f' in k: - font = k['f'] - fontSize=mainSize - if 'fs' in k: - fontSize = k['fs'] - print("Generating: {} {}".format(name, legend)) - cap = opk.keycap(angle=r['angle'], height=r['height'], unitX=k['w'], - convex=convex, depth=depth, - legend=legend, font=font, - fontsize=fontSize) - # Export one key at the time - #exporters.export(cap, './export/STEP/' + name + '.step') - #exporters.export(cap, './export/STL/' + name + '.stl', tolerance=0.001, angularTolerance=0.05) - w = 19.05 * k['w'] / 2 - x+= w - assy.add(cap, name=name, color=cq.Color(choice(colours)), - loc=cq.Location(cq.Vector(x,y,0))) - x+= w - y -= 19.05 +sx = 19.05 +sy = 19.05 + +assy = render_kb(rows, mainFont=mainFont, mainSize = mainSize, sx = sx, sy = sy) if 'show_object' in locals(): show_object(assy) diff --git a/examples/numpad.py b/examples/numpad.py index 5aef720..a5ea0d3 100644 --- a/examples/numpad.py +++ b/examples/numpad.py @@ -1,7 +1,7 @@ import opk import cadquery as cq from cadquery import exporters -from random import choice +from kb_render import * keys = { 0: [ @@ -32,7 +32,6 @@ { 't':'.' } ], } -colours=["tomato2","springgreen3","slateblue2","sienna1","seagreen3","orangered2","orchid2","maroon2","limegreen","lightseagreen","lightcoral","magenta3","yellow"] #rows = [ #{'angle': 13, 'height': 16, 'keys': keys[0] }, # row 0, function row @@ -52,58 +51,11 @@ mainFont = "DejaVu Sans Mono" mainSize = 9 -assy = cq.Assembly() -y = 0 -for i, r in enumerate(rows): - x = 0 - for j,k in enumerate(r['keys']): - kh = 1 - if 'h' in k: - kh = k['h'] - kw = 1 - if 'w' in k: - kw = k['w'] - name = "row{}_{}_U{}".format(i,j,kw) - convex = False - if 'convex' in k: - convex = k['convex'] - name+= "_space" +sx = 19.05 +sy = 19.05 - depth = 2.8 - if 'n' in k: - if k['n']: - name+= "_homing" - depth = 3.6 - legend = '' - if 't' in k: - legend = k['t'] - font = mainFont - if 'f' in k: - font = k['f'] - fontSize=mainSize - if 'fs' in k: - fontSize = k['fs'] - - print("Generating: {} {}".format(name, legend)) - cap = opk.keycap(angle=r['angle'], height=r['height'], - unitX=kw, unitY=kh, - convex=convex, depth=depth, - legend=legend, font=font, - fontsize=fontSize) - # Export one key at the time - #exporters.export(cap, './export/STEP/' + name + '.step') - #exporters.export(cap, './export/STL/' + name + '.stl', tolerance=0.001, angularTolerance=0.05) - w = 19.05 * kw / 2 - x+= w - oy = 0.0 - if 'oy' in k: - oy = k['oy'] - y += oy*19.05 - assy.add(cap, name=name, color=cq.Color(choice(colours)), - loc=cq.Location(cq.Vector(x,y,0))) - x+= w - y = -(i+1)*19.05 +assy = render_kb(rows, mainFont=mainFont, mainSize = mainSize, sx = sx, sy = sy) if 'show_object' in locals(): show_object(assy) diff --git a/examples/planck.py b/examples/planck.py index ffee957..5529570 100644 --- a/examples/planck.py +++ b/examples/planck.py @@ -1,58 +1,93 @@ -#!/usr/bin/env python3 - +import opk import cadquery as cq -from opk import * - -leg = [ - ["⎋", "q","w", "e","r","t","y","u","i","o","p","⌫"], - ["↹","a","s","d","f","g","h","j","k","l",":\n;","@\n'"], - ["⇧","z","x","c","v","b","n","m","<\n,",">\n.","?\n/","⌅"], - ["⎈","⎇","","⎇","⇓","","⇑","←","↓","↑","→"]] -lay= [[1]*12, [1]*12,[1]*12,[1]*5+[2]+[1]*5] -fonts=[ - ["DejaVu Sans Mono"]*12, - ["DejaVu Sans Mono"]*12, - ["DejaVu Sans Mono"]*12, - ["DejaVu Sans Mono"]*11 - ] -sx=19.05 -sy=19.05 -planck = cq.Assembly() -y = 0 -i = -1 -j = -1 -angles=[8.5,-6,-7,0] -vfs=[0,9,7,6,4.5,4.5] - -for row,ll,ff in zip(leg,lay,fonts): - y -= sy - i += 1 - x = 0 - for k,l,f in zip(row,ll,ff): - print(k,l) - w = l*sx/2.0 - j += 1 - x += w - convex=False - if k == '': - convex=True - scoop = 2.5 - if k in ['f','F','j','J']: - scoop = 2.5*1.2 - fs=3 - if len(k)<=5: - fs=vfs[len(k)] - if (len(k.split("\n"))==2): - fs = 4.5 - planck.add(keycap(legend=k, - angle=angles[i], - font=f, - depth = scoop, - convex=convex, - fontsize = fs, - unitX=l), - name="k{}{}".format(i,j), - loc=cq.Location(cq.Vector(x,y,0))) - x += w -#cq.exporters.export(planck.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) -show_object(planck, name="pre", options={"alpha": 0}) +from cadquery import exporters +from kb_render import * + +keys = { + 0: [ + { 't':'⎋','fs':12}, + { 't':'q' }, + { 't':'w' }, + { 't':'e' }, + { 't':'r' }, + { 't':'t' }, + { 't':'y' }, + { 't':'u' }, + { 't':'i' }, + { 't':'o' }, + { 't':'p' }, + { 't':'⌫','fs':12} + ], + 1: [ + { 't':'↹','fs':12 }, + { 't':'a' }, + { 't':'s' }, + { 't':'d' }, + { 't':'f','n': True }, + { 't':'g' }, + { 't':'h' }, + { 't':'j','n': True }, + { 't':'k' }, + { 't':'l' }, + {'t':';\n:','fs':5}, + { 't':'\'\n@','fs':5 } + ], + 2: [ + { 't':'⇧','fs':12 }, + { 't':'z' }, + { 't':'x' }, + { 't':'c' }, + { 't':'v' }, + { 't':'b' }, + { 't':'n' }, + { 't':'m' }, + { 't':',\n<','fs':5 }, + { 't':'.\n>','fs':5 }, + { 't':'/\n?','fs':5 }, + { 't':'⊼','fs':5 } + ], + 3: [ + { 't':'⎈','f':"DejaVu Sans Mono",'fs':12 }, + { 't':'','f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf" }, + { 't':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, + { 't':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, + { 't':'⇓','f':"DejaVu Sans Mono",'fs':12}, + {'w':2,'convex':True}, + { 't':'⇑','f':"DejaVu Sans Mono",'fs':12 }, + { 't':'←','fs':12 }, + { 't':'↑','fs':12 }, + { 't':'↓','fs':12 }, + { 't':'→','fs':12 } + + ], +} + +#rows = [ + #{'angle': 13, 'height': 16, 'keys': keys[0] }, # row 0, function row + #{'angle': 9, 'height': 14, 'keys': keys[1] }, # row 1, numbers row + #{'angle': 8, 'height': 12, 'keys': keys[2] }, # row 2, QWERT + #{'angle': -6, 'height': 11.5, 'keys': keys[3] }, # row 3, ASDFG + #{'angle': -8, 'height': 13, 'keys': keys[4] }, # row 4, ZXCVB + #{'angle': 0, 'height': 12.5, 'keys': keys[5] }, # row 5, bottom row +#] +rows = [ + {'angle': 8, 'height': 12, 'keys': keys[0] }, # row 2, QWERT + {'angle': -6, 'height': 11.5, 'keys': keys[1] }, # row 3, ASDFG + {'angle': -8, 'height': 13, 'keys': keys[2] }, # row 4, ZXCVB + {'angle': 0, 'height': 12.5, 'keys': keys[3] }, # row 5, bottom row +] + +mainFont = "DejaVu Sans Mono" +mainSize = 9 + +sx = 19.05 +sy = 19.05 + +assy = render_kb(rows, mainFont=mainFont, mainSize = mainSize, sx = sx, sy = sy) + +if 'show_object' in locals(): + show_object(assy) + +# Export the whole assembly, very handy especially for STEP +#exporters.export(assy.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +#exporters.export(assy.toCompound(), 'keycaps.step') From eef58857c4c32c241e033bc61117bc9588ae586d Mon Sep 17 00:00:00 2001 From: alin m elena Date: Sun, 11 Sep 2022 20:29:56 +0100 Subject: [PATCH 40/44] update --- examples/ansi.py | 159 ++++++++++++++++++------------ examples/ansi68.py | 168 ++++++++++++++++++++------------ examples/full-ansi.py | 220 ++++++++++++++++++++++++++---------------- examples/kb_render.py | 7 +- examples/m65.py | 131 +++++++++++++------------ examples/numpad.py | 22 ++--- examples/planck.py | 95 +++++++++--------- examples/preonic.py | 161 ++++++++++++++++++++----------- examples/tkl.py | 203 ++++++++++++++++++++++---------------- 9 files changed, 697 insertions(+), 469 deletions(-) diff --git a/examples/ansi.py b/examples/ansi.py index 2bb7923..a1bc312 100644 --- a/examples/ansi.py +++ b/examples/ansi.py @@ -1,64 +1,101 @@ -#!/usr/bin/env python3 - +import opk import cadquery as cq -from opk import * -leg = [["⎋","!\n1","\"\n2","£\n3","$\n4","%\n5","^\n6","&\n7","*\n8","(\n9",")\n0","_\n-","=\n+","⌫"], -["↹","q","w","e","r","t","y","u","i","o","p","{\n[","}\n]","|\n\\"], -["Caps","a","s","d","f","g","h","j","k","l",":\n;","@\n'","enter"], -["⇧","z","x","c","v","b","n","m","<\n,",">\n.","?\n/","⇧"], -["⎈","","Alt","","Alt","Fn","Menu","Ctrl"]] -lay= [[1]*13+[2], - [1.5]+[1]*12+[1.5], - [1.75]+[1]*11+[2.25], - [2.25]+[1]*10+[2.75], - [1.25,1.25,1.25,6.25,1.25,1.25,1.25,1.25]] -fonts=[ - ["DejaVu Sans Mono"]*14, - ["DejaVu Sans Mono"]*14, - ["DejaVu Sans Mono"]*13, - ["DejaVu Sans Mono"]*12, - ["DejaVu Sans Mono"]+["Font Awesome 6 Brands"]+["DejaVu Sans Mono"]*6 - ] -sx=19.05 -sy=19.05 -m65 = cq.Assembly() -y = 0 -i = -1 -j = -1 -angles=[9,8.5,-6,-7,0] -vfs=[0,9,7,6,4.5,4.5] +from cadquery import exporters +from kb_render import * + +keys = { + 0: [ + { 't':'⎋','fs':12}, + { 't':'!\n1','fs':5}, + { 't':'"\n2','fs':5}, + { 't':'£\n3','fs':5}, + { 't':'$\n4','fs':5}, + { 't':'%\n5','fs':5}, + { 't':'^\n6','fs':5}, + { 't':'&\n7','fs':5}, + { 't':'*\n8','fs':5}, + { 't':'(\n9','fs':5}, + { 't':')\n0','fs':5}, + { 't':'-\n_','fs':5}, + { 't':'+\n=','fs':5}, + { 'w':2,'t':'⌫','fs':12}, + ], + 1: [ + { 'w':1.5,'t':'↹','fs':12}, + { 't':'q' }, + { 't':'w' }, + { 't':'e' }, + { 't':'r' }, + { 't':'t' }, + { 't':'y' }, + { 't':'u' }, + { 't':'i' }, + { 't':'o' }, + { 't':'p' }, + { 't':'[\n{','fs':5 }, + { 't':']\n}','fs':5 }, + { 'w':1.5,'t':'|\n\\','fs':5 }, + ], + 2: [ + { 'w':1.75,'t':'⇪' }, + { 't':'a' }, + { 't':'s' }, + { 't':'d' }, + { 't':'f','n': True }, + { 't':'g' }, + { 't':'h' }, + { 't':'j','n': True }, + { 't':'k' }, + { 't':'l' }, + { 't':';\n:','fs':5 }, + { 't':'\'\n@','fs':5 }, + { 'w':2.25,'t':'⊼','fs':12 } + ], + 3: [ + { 'w':2.25,'t':'⇧' }, + { 't':'z' }, + { 't':'x' }, + { 't':'c' }, + { 't':'v' }, + { 't':'b' }, + { 't':'n' }, + { 't':'m' }, + { 't':',\n<','fs':5 }, + { 't':'.\n>','fs':5 }, + { 't':'/\n?','fs':5 }, + { 'w':2.75,'t':'⇧'}, + ], + 4: [ + { 'w': 1.25,'t':'⎈'}, + { 'w': 1.25,'t':'','f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf"}, + { 'w': 1.25,'t':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf"}, + { 'w': 6.25, 'convex':True}, + { 'w':1.25, 't':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, + {'w':1.25, 't':'Fn'}, + { 'w': 1.25,'t':'','f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf" }, + { 'w': 1.25,'t':'⎈'}, + ], +} + +rows = [ + {'angle': 9, 'height': 14, 'keys': keys[0] }, # row 1, numbers row + {'angle': 8, 'height': 12, 'keys': keys[1] }, # row 2, QWERT + {'angle': -6, 'height': 11.5, 'keys': keys[2] }, # row 3, ASDFG + {'angle': -8, 'height': 13, 'keys': keys[3] }, # row 4, ZXCVB + {'angle': 0, 'height': 12.5, 'keys': keys[4] }, # row 5, bottom row +] + +mainFont = "DejaVu Sans Mono" +mainSize = 9 + +sx = 19.05 +sy = 19.05 + +assy = render_kb(rows, mainFont=mainFont, mainSize = mainSize, sx = sx, sy = sy) -for row,ll,ff in zip(leg,lay,fonts): - y -= sy - i += 1 - x = 0 - for k,l,f in zip(row,ll,ff): - print(k,l) - w = l*sx/2.0 - j += 1 - x += w - convex=False - if k == '': - convex=True - scoop = 2.5 - if k in ['f','F','j','J']: - scoop = 2.5*1.2 - fs=3 - if len(k)<=5: - fs=vfs[len(k)] - if (len(k.split("\n"))==2): - fs = 4.5 - m65.add(keycap(legend=k, - angle=angles[i], - font=f, - convex=convex, - depth = scoop, - fontsize = fs, - unitX=l), - name="k{}{}".format(i,j), - loc=cq.Location(cq.Vector(x,y,0)) - ) - x += w +if 'show_object' in locals(): + show_object(assy) -#cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) -show_object(m65, name="legend", options={'color': 'red', 'alpha': 0}) +# Export the whole assembly, very handy especially for STEP +#exporters.export(assy.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +#exporters.export(assy.toCompound(), 'keycaps.step') diff --git a/examples/ansi68.py b/examples/ansi68.py index f7a57e6..2af44f7 100644 --- a/examples/ansi68.py +++ b/examples/ansi68.py @@ -1,64 +1,110 @@ -#!/usr/bin/env python3 - +import opk import cadquery as cq -from opk import * -leg = [["⎋","!\n1","\"\n2","£\n3","$\n4","%\n5","^\n6","&\n7","*\n8","(\n9",")\n0","_\n-","=\n+","⌫","¬\n`"], -["↹","q","w","e","r","t","y","u","i","o","p","[","]","|\n\\","⌦"], -["⇪","a","s","d","f","g","h","j","k","l",":\n;","@\n'","enter","Pg\nUp"], -["⇧","z","x","c","v","b","n","m","<\n,",">\n.","?\n/","⇧","↑","Pg\nDn"], -["⎈","","⎇","","⎇","Fn","⎈","←","↓","→"]] -lay= [[1]*13+[2,1], - [1.5]+[1]*12+[1.5]+[1], - [1.75]+[1]*11+[2.25]+[1], - [2.25]+[1]*10+[1.75]+[1,1], - [1.25,1.25,1.25,6.25,1,1,1,1,1,1]] -fonts=[ - ["DejaVu Sans Mono"]*15, - ["DejaVu Sans Mono"]*15, - ["DejaVu Sans Mono"]*14, - ["DejaVu Sans Mono"]*14, - ["DejaVu Sans Mono"]+["Font Awesome 6 Brands"]+["DejaVu Sans Mono"]*8 - ] -sx=19.05 -sy=19.05 -m65 = cq.Assembly() -y = 0 -i = -1 -j = -1 -angles=[9,8.5,-6,-7,0] -vfs=[0,9,7,6,4.5,4.5] +from cadquery import exporters +from kb_render import * + +keys = { + 0: [ + { 't':'⎋','fs':12}, + { 't':'!\n1','fs':5}, + { 't':'"\n2','fs':5}, + { 't':'£\n3','fs':5}, + { 't':'$\n4','fs':5}, + { 't':'%\n5','fs':5}, + { 't':'^\n6','fs':5}, + { 't':'&\n7','fs':5}, + { 't':'*\n8','fs':5}, + { 't':'(\n9','fs':5}, + { 't':')\n0','fs':5}, + { 't':'-\n_','fs':5}, + { 't':'+\n=','fs':5}, + { 'w':2,'t':'⌫','fs':12}, + { 't':'`\n¬','fs':5} + ], + 1: [ + { 'w':1.5,'t':'↹','fs':12}, + { 't':'q' }, + { 't':'w' }, + { 't':'e' }, + { 't':'r' }, + { 't':'t' }, + { 't':'y' }, + { 't':'u' }, + { 't':'i' }, + { 't':'o' }, + { 't':'p' }, + { 't':'[\n{','fs':5 }, + { 't':']\n}','fs':5 }, + { 'w':1.5,'t':'|\n\\','fs':5 }, + { 't':'⌦' } + ], + 2: [ + { 'w':1.75,'t':'⇪' }, + { 't':'a' }, + { 't':'s' }, + { 't':'d' }, + { 't':'f','n': True }, + { 't':'g' }, + { 't':'h' }, + { 't':'j','n': True }, + { 't':'k' }, + { 't':'l' }, + { 't':';\n:','fs':5 }, + { 't':'\'\n@','fs':5 }, + { 'w':2.25,'t':'⊼','fs':12 }, + {'t':"Pg\nUp",'fs':5} + ], + 3: [ + { 'w':2.25,'t':'⇧' }, + { 't':'z' }, + { 't':'x' }, + { 't':'c' }, + { 't':'v' }, + { 't':'b' }, + { 't':'n' }, + { 't':'m' }, + { 't':',\n<','fs':5 }, + { 't':'.\n>','fs':5 }, + { 't':'/\n?','fs':5 }, + { 'w':1.75,'t':'⇧'}, + { 't':'↑','fs':12}, + {'t':"Pg\nDn",'fs':5} + + + ], + 4: [ + { 'w': 1.25,'t':'⎈'}, + { 'w': 1.25,'t':'','f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf"}, + { 'w': 1.25,'t':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf"}, + { 'w': 6.25, 'convex':True}, + { 't':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, + { 't':'','f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf" }, + { 't':'⎈'}, + { 't':'←','fs':12}, + { 't':'↓','fs':12}, + { 't':'→','fs':12}, + ], +} + +rows = [ + {'angle': 9, 'height': 14, 'keys': keys[0] }, # row 1, numbers row + {'angle': 8, 'height': 12, 'keys': keys[1] }, # row 2, QWERT + {'angle': -6, 'height': 11.5, 'keys': keys[2] }, # row 3, ASDFG + {'angle': -8, 'height': 13, 'keys': keys[3] }, # row 4, ZXCVB + {'angle': 0, 'height': 12.5, 'keys': keys[4] }, # row 5, bottom row +] + +mainFont = "DejaVu Sans Mono" +mainSize = 9 + +sx = 19.05 +sy = 19.05 + +assy = render_kb(rows, mainFont=mainFont, mainSize = mainSize, sx = sx, sy = sy) -for row,ll,ff in zip(leg,lay,fonts): - y -= sy - i += 1 - x = 0 - for k,l,f in zip(row,ll,ff): - print(k,l) - w = l*sx/2.0 - j += 1 - x += w - convex=False - if k == '': - convex=True - scoop = 2.5 - if k in ['f','F','j','J']: - scoop = 2.5*1.2 - fs=3 - if len(k)<=5: - fs=vfs[len(k)] - if (len(k.split("\n"))==2): - fs = 4.5 - m65.add(keycap(legend=k, - angle=angles[i], - font=f, - convex=convex, - depth = scoop, - fontsize = fs, - unitX=l), - name="k{}{}".format(i,j), - loc=cq.Location(cq.Vector(x,y,0)) - ) - x += w +if 'show_object' in locals(): + show_object(assy) -#cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) -show_object(m65, name="legend", options={'color': 'red', 'alpha': 0}) +# Export the whole assembly, very handy especially for STEP +#exporters.export(assy.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +#exporters.export(assy.toCompound(), 'keycaps.step') diff --git a/examples/full-ansi.py b/examples/full-ansi.py index 48d1769..dc6f259 100644 --- a/examples/full-ansi.py +++ b/examples/full-ansi.py @@ -1,89 +1,147 @@ -#!/usr/bin/env python3 - +import opk import cadquery as cq -from opk import * -leg = [ ["⎋","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12", "Prn\nScr","Scr\nLock","Pause"], - ["¬\n`","!\n1","\"\n2","£\n3","$\n4","%\n5","^\n6","&\n7","*\n8","(\n9",")\n0","_\n-","=\n+","⌫","Ins","Home","Pg\nUp","Num","/","*","-"], -["↹","q","w","e","r","t","y","u","i","o","p","[","]","|\n\\","Del","End","Pg\nDn","7","8","9","+"], -["⇪","a","s","d","f","g","h","j","k","l",";","'","enter","4","5","6","+"], -["⇧","z","x","c","v","b","n","m",",",".","/","⇧","↑","1","2","3","Ent"], -["⎈","","Alt","","Alt","Fn","Mnu","Ctrl","←","↓","→","0",".","Ent"]] -lay= [[1]*16, - [1]*13+[2]+[1]*7, - [1.5]+[1]*12+[1.5]+[1]*7, - [1.75]+[1]*11+[2.25]+[1]*4, - [2.25]+[1]*10+[2.75]+[1]*5, - [1.25,1.25,1.25,6.25,1.25,1.25,1.25,1.25,1,1,1,2,1,1]] -fonts=[ - ["DejaVu Sans Mono"]*16, - ["DejaVu Sans Mono"]*21, - ["DejaVu Sans Mono"]*21, - ["DejaVu Sans Mono"]*17, - ["DejaVu Sans Mono"]*17, - ["DejaVu Sans Mono"]+["Font Awesome 6 Brands"]+["Noto Sans"]*6+["DejaVu Sans Mono"]*6 - ] - -offx=[[0,1,0,0,0]+[0.5,0,0,0]+[0.5,0,0,0]+[0.5,0,0], - [0.0]*14+[0.5,0,0,0.5,0.0,0,0], - [0.0]*14+[0.5,0,0,0.5,0,0,0], - [0.0]*13+[4,0,0,0], - [0.0]*12+[1.5,1.5,0,0,0], - [0.0]*8+[0.5,0,0,0.5,0,0] - ] -offy=[[0.5]+[0]*15, - [0]*21, - [0]*21, - [0]*17, - [0]*17, - [0]*14 - ] - -sx=19.05 -sy=19.05 +from cadquery import exporters +from kb_render import * -m65 = cq.Assembly() +keys = { + 0: [ + {'t':'⎋'}, + {'t':'F1','ox':1.0,'fs':6}, + {'t':'F2','fs':6}, + {'t':'F3','fs':6}, + {'t':'F4','fs':6}, + {'t':'F5','ox':0.5,'fs':6}, + {'t':'F6','fs':6}, + {'t':'F7','fs':6}, + {'t':'F8','fs':6}, + {'t':'F9','ox':0.5,'fs':6}, + {'t':'F10','fs':6}, + {'t':'F11','fs':6}, + {'t':'F12','fs':6}, + {'t':'Print\nScreen','ox':0.5,'fs':3}, + {'t':'Screen\nLock','fs':3}, + {'t':'Pause','fs':3} + ], + 1: [ + { 't':'`\n¬','fs':5, 'oy':-0.5}, + { 't':'!\n1','fs':5}, + { 't':'"\n2','fs':5}, + { 't':'£\n3','fs':5}, + { 't':'$\n4','fs':5}, + { 't':'%\n5','fs':5}, + { 't':'^\n6','fs':5}, + { 't':'&\n7','fs':5}, + { 't':'*\n8','fs':5}, + { 't':'(\n9','fs':5}, + { 't':')\n0','fs':5}, + { 't':'-\n_','fs':5}, + { 't':'+\n=','fs':5}, + { 'w':2,'t':'⌫','fs':12}, + { 't':'Ins','fs':5,'ox':0.5}, + { 't':'Home','fs':5}, + { 't':'Pg\nUp','fs':5}, + { 't':'Num','fs':7,'ox':0.5}, + { 't':'/'}, + { 't':'*' }, + { 't':'-'} + ], + 2: [ + { 'w':1.5,'t':'↹','fs':12,'oy':-0.5}, + { 't':'q' }, + { 't':'w' }, + { 't':'e' }, + { 't':'r' }, + { 't':'t' }, + { 't':'y' }, + { 't':'u' }, + { 't':'i' }, + { 't':'o' }, + { 't':'p' }, + { 't':'[\n{','fs':5 }, + { 't':']\n}','fs':5 }, + { 'w':1.5,'t':'|\n\\','fs':5 }, + { 't':'Del','ox':0.5,'fs':5 }, + { 't':'End','fs':5 }, + { 't':'Pg\nDn','fs':5}, + { 't':'7','ox':0.5 }, + { 't':'8' }, + { 't':'9' }, + { 't':'+','h':2,'oy':-0.5 } + ], + 3: [ + { 'w':1.75,'t':'⇪','oy':-0.5 }, + { 't':'a' }, + { 't':'s' }, + { 't':'d' }, + { 't':'f','n': True }, + { 't':'g' }, + { 't':'h' }, + { 't':'j','n': True }, + { 't':'k' }, + { 't':'l' }, + { 't':';\n:','fs':5 }, + { 't':'\'\n@','fs':5 }, + { 'w':2.25,'t':'⊼','fs':12 }, + { 't':'4','ox':4.0 }, + { 't':'5','n':True }, + { 't':'6' } + ], + 4: [ + { 'w':2.25,'t':'⇧','oy':-0.5 }, + { 't':'z' }, + { 't':'x' }, + { 't':'c' }, + { 't':'v' }, + { 't':'b' }, + { 't':'n' }, + { 't':'m' }, + { 't':',\n<','fs':5 }, + { 't':'.\n>','fs':5 }, + { 't':'/\n?','fs':5 }, + { 'w':2.75,'t':'⇧'}, + { 't':'↑','fs':12,'ox':1.5}, + { 't':'1','ox':1.5 }, + { 't':'2' }, + { 't':'3' }, + { 't':'⊼','h':2,'oy':-0.5 }, + ], + 5: [ + { 'w': 1.25,'t':'⎈','oy':-0.5 }, + { 'w': 1.25,'t':'','f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf"}, + { 'w': 1.25,'t':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf"}, + { 'w': 6.25, 'convex':True}, + { 'w':1.25, 't':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, + {'w':1.25, 't':'Fn'}, + { 'w': 1.25,'t':'','f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf" }, + { 'w': 1.25,'t':'⎈'}, + { 't':'←','fs':12,'ox': 0.5}, + { 't':'↓','fs':12 }, + { 't':'→','fs':12 }, + { 'w': 2,'t':'0','ox':0.5 }, + { 't':'.' } + ], +} -y = 0 -i = -1 -j = -1 +rows = [ + {'angle': 13, 'height': 16, 'keys': keys[0] }, # row 0, function row + {'angle': 9, 'height': 14, 'keys': keys[1] }, # row 1, numbers row + {'angle': 8, 'height': 12, 'keys': keys[2] }, # row 2, QWERT + {'angle': -6, 'height': 11.5, 'keys': keys[3] }, # row 3, ASDFG + {'angle': -8, 'height': 13, 'keys': keys[4] }, # row 4, ZXCVB + {'angle': 0, 'height': 12.5, 'keys': keys[5] }, # row 5, bottom row +] -angles=[13.5,9,8.5,-6,-7,0] -vfs=[0,9,7,6,4.5,4.5] +mainFont = "DejaVu Sans Mono" +mainSize = 9 -for row,ll,ff,ofx,ofy in zip(leg,lay,fonts,offx,offy): +sx = 19.05 +sy = 19.05 - i += 1 - y = -(i+1)*sy +assy = render_kb(rows, mainFont=mainFont, mainSize = mainSize, sx = sx, sy = sy) - x = 0 - for k,l,f,ox,oy in zip(row,ll,ff,ofx,ofy): - print(k,len(k),l,f) - w = l*sx/2.0 - j += 1 - x += w + ox*sx - y += oy*sy - convex=False - if k == '': - convex=True - scoop = 2.5 - if k in ['f','F','j','J']: - scoop = 2.5*1.2 - fs=3 - if len(k)<=5: - fs=vfs[len(k)] - if (len(k.split("\n"))==2): - fs = 4.5 - m65.add(keycap(legend=k, - angle=angles[i], - font=f, - convex=convex, - depth = scoop, - fontsize=fs, - unitX=l), - name="k{}{}".format(i,j), - loc=cq.Location(cq.Vector(x,y,0)) - ) - x += w +if 'show_object' in locals(): + show_object(assy) -#cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) -show_object(m65, name="legend", options={'color': 'red', 'alpha': 0}) +# Export the whole assembly, very handy especially for STEP +#exporters.export(assy.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +#exporters.export(assy.toCompound(), 'keycaps.step') diff --git a/examples/kb_render.py b/examples/kb_render.py index ac76b89..8f5fa81 100644 --- a/examples/kb_render.py +++ b/examples/kb_render.py @@ -2,7 +2,7 @@ from random import choice import opk -def render_kb(rows, mainFont="DejaVu Sans Mono", mainSize = 9, sx = 19.05, sy = 19.05, depth = 2.8): +def render_kb(rows, mainFont="DejaVu Sans Mono", mainSize = 9, sx = 19.05, sy = 19.05, depth = 2.8, export = False ): assy = cq.Assembly() colours=["tomato2","springgreen3","slateblue2","sienna1","seagreen3","orangered2","orchid2","maroon2","limegreen","lightseagreen","lightcoral","magenta3","yellow"] @@ -44,8 +44,9 @@ def render_kb(rows, mainFont="DejaVu Sans Mono", mainSize = 9, sx = 19.05, sy = legend=legend, font=font, fontsize=fontSize) # Export one key at the time - #exporters.export(cap, './export/STEP/' + name + '.step') - #exporters.export(cap, './export/STL/' + name + '.stl', tolerance=0.001, angularTolerance=0.05) + if export: + cq.exporters.export(cap, './export/STEP/' + name + '.step') + cq.exporters.export(cap, './export/STL/' + name + '.stl', tolerance=0.001, angularTolerance=0.05) w = sx * kw / 2 ox = 0.0 if 'ox' in k: diff --git a/examples/m65.py b/examples/m65.py index e20da30..e9c7a89 100644 --- a/examples/m65.py +++ b/examples/m65.py @@ -5,80 +5,79 @@ keys = { 0: [ - { 't':'⎋ `\n ¬','fs':5}, - { 't':'1\n!','fs':5 }, - { 't':'2\n\"','fs':5 }, - { 't':'3\n£','fs':5 }, - { 't':'4\n$','fs':5 }, - { 't':'5\n%','fs':5 }, - { 't':'6\n^','fs':5 }, - { 't':'7\n&','fs':5 }, - { 't':'8\n*','fs':5 }, - { 't':'9\n(','fs':5 }, - { 't':'0\n)','fs':5 }, - { 't':'- ⌦\n_ ','fs':5 }, - { 't':'⌫ =\n +','fs':5 } + { 't':'⎋ `\n ¬','fs':5}, + { 't':'1\n!','fs':5 }, + { 't':'2\n\"','fs':5 }, + { 't':'3\n£','fs':5 }, + { 't':'4\n$','fs':5 }, + { 't':'5\n%','fs':5 }, + { 't':'6\n^','fs':5 }, + { 't':'7\n&','fs':5 }, + { 't':'8\n*','fs':5 }, + { 't':'9\n(','fs':5 }, + { 't':'0\n)','fs':5 }, + { 't':'- ⌦\n_ ','fs':5 }, + { 't':'⌫ =\n +','fs':5 } ], 1: [ - { 't':'↹','fs':12}, - { 't':'q' }, - { 't':'w' }, - { 't':'e' }, - { 't':'r' }, - { 't':'t' }, - { 't':'y' }, - { 't':'u' }, - { 't':'i' }, - { 't':'o' }, - { 't':'p' }, - { 't':'[\n{','fs':5 }, - { 't':']\n}','fs':5 } + { 't':'↹','fs':12}, + { 't':'q σ\nâ ϕ','fs':5 }, + { 't':'w ω\n Ω','fs':5 }, + { 't':'e ε\n ℇ','fs':5 }, + { 't':'r ρ\n ∇','fs':5 }, + { 't':'t ϑ\nț θ','fs':5 }, + { 't':'y ℝ\n ℤ','fs':5 }, + { 't':'u τ\n ℂ','fs':5 }, + { 't':'i ∫\nî ∮','fs':5 }, + { 't':'o ∞\n ⊗','fs':5 }, + { 't':'p π\n ∏','fs':5 }, + { 't':'[ ⋜\n{ ≅','fs':5 }, + { 't':'] ⋝\n} ≅','fs':5 } ], 2: [ - { 't':'#\n~','fs':5 }, - { 't':'a' }, - { 't':'s' }, - { 't':'d' }, - { 't':'f','n': True }, - { 't':'g' }, - { 't':'h' }, - { 't':'j','n': True }, - { 't':'k' }, - { 't':'l' }, - { 't':';\n:','fs':5 }, - { 't':'\'\n@','fs':5 }, - { 't':'⊼','fs':12 }, + { 't':'# \n~ ⇪','fs':5 }, + { 't':'a α\nă ̇','fs':5 }, + { 't':'s ∑\nș ⨋','fs':5 }, + { 't':'d δ\n ∂','fs':5 }, + { 't':'f φ\n ψ','n': True,'fs':5 }, + { 't':'g γ\n Γ','fs':5 }, + { 't':'h ℏ\n 𝓗','fs':5 }, + { 't':'j ∈\n ∉','n': True,'fs':5 }, + { 't':'k ϰ\n ∆','fs':5 }, + { 't':'l λ\n Λ','fs':5 }, + { 't':'; 𝔼\n: Å','fs':5 }, + { 't':'\' ∝\n@ ℒ','fs':5 }, + { 't':'⊼','fs':12 } ], 3: [ - { 't':'⇧','fs':12 }, - { 't':'\\\n|','fs':5 }, - { 't':'z' }, - { 't':'x' }, - { 't':'c' }, - { 't':'v' }, - { 't':'b' }, - { 't':'n' }, - { 't':'m' }, - { 't':',\n<','fs':5 }, - { 't':'.\n>','fs':5 }, - { 't':'↑','fs':12 }, - { 't':'/\n?','fs':5 } + { 't':'⇧','fs':12 }, + { 't':'\\ ≡\n| ≢','fs':5 }, + { 't':'z ζ\n ∡','fs':5 }, + { 't':'x ξ\nç Ξ','fs':5 }, + { 't':'c χ\n⊄ ⊂','fs':5 }, + { 't':'v ν\n⊅ ⊃','fs':5 }, + { 't':'b β\n∧ ∩','fs':5 }, + { 't':'n η\n∨ ∪','fs':5 }, + { 't':'m μ\n ∘','fs':5 }, + { 't':', ≈\n< ≉','fs':5 }, + { 't':'. ±\n> ∓','fs':5 }, + { 't':'↑','fs':12}, + { 't':'/ ×\n? ⋅','fs':5 } ], 4: [ - { 't':'⎈','f':"DejaVu Sans Mono",'fs':12 }, - { 't':'','f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf" }, - { 't':'⇓','f':"DejaVu Sans Mono",'fs':12}, - { 't':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, - { 't':'⇑','f':"DejaVu Sans Mono",'fs':12 }, - { 'convex':True}, - { 't':'','convex':True,'f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf",'fs':9}, - { 'convex':True}, - { 't':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, - { 't':'⇧','fs':12 }, - { 't':'←','fs':12 }, - { 't':'↓','fs':12 }, - { 't':'→','fs':12 } - + { 't':'⎈','f':"DejaVu Sans Mono",'fs':12 }, + { 't':'','f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf" }, + { 't':'⇓','f':"DejaVu Sans Mono",'fs':12}, + { 't':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, + { 't':'⇑','f':"DejaVu Sans Mono",'fs':12 }, + { 'convex':True}, + { 't':'','convex':True,'f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf",'fs':9}, + { 'convex':True}, + { 't':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, + { 't':'⇧','fs':12 }, + { 't':'←','fs':12 }, + { 't':'↓','fs':12 }, + { 't':'→','fs':12 } ], } diff --git a/examples/numpad.py b/examples/numpad.py index a5ea0d3..5e3a5f4 100644 --- a/examples/numpad.py +++ b/examples/numpad.py @@ -11,25 +11,25 @@ { 't':'-'} ], 1: [ - { 't':'7' }, - { 't':'8' }, - { 't':'9' }, + { 't':'7\nHome','fs':4.5 }, + { 't':'8\n↑','fs':4.5 }, + { 't':'9\nPgUp','fs':4.5 }, { 't':'+','h':2,'oy':-0.5 } ], 2: [ - { 't':'4' }, + { 't':'4\n←','fs':4.5 }, { 't':'5','n':True }, - { 't':'6' } + { 't':'6\n→','fs':4.5 } ], 3: [ - { 't':'1' }, - { 't':'2' }, - { 't':'3' }, + { 't':'1\nEnd','fs':4.5 }, + { 't':'2\n↓','fs':4.5 }, + { 't':'3\nPgDn','fs':4.5 }, { 't':'⊼','h':2,'oy':-0.5 }, ], 4: [ - { 'w': 2,'t':'0' }, - { 't':'.' } + { 'w': 2,'t':'0\nIns','fs':6 }, + { 't':'.\nDel', 'fs':5 } ], } @@ -55,7 +55,7 @@ sx = 19.05 sy = 19.05 -assy = render_kb(rows, mainFont=mainFont, mainSize = mainSize, sx = sx, sy = sy) +assy = render_kb(rows, mainFont=mainFont, mainSize = mainSize, sx = sx, sy = sy, export=True) if 'show_object' in locals(): show_object(assy) diff --git a/examples/planck.py b/examples/planck.py index 5529570..bee89bd 100644 --- a/examples/planck.py +++ b/examples/planck.py @@ -5,60 +5,59 @@ keys = { 0: [ - { 't':'⎋','fs':12}, - { 't':'q' }, - { 't':'w' }, - { 't':'e' }, - { 't':'r' }, - { 't':'t' }, - { 't':'y' }, - { 't':'u' }, - { 't':'i' }, - { 't':'o' }, - { 't':'p' }, - { 't':'⌫','fs':12} + { 't':'⎋','fs':12}, + { 't':'q' }, + { 't':'w' }, + { 't':'e' }, + { 't':'r' }, + { 't':'t' }, + { 't':'y' }, + { 't':'u' }, + { 't':'i' }, + { 't':'o' }, + { 't':'p' }, + { 't':'⌫','fs':12} ], 1: [ - { 't':'↹','fs':12 }, - { 't':'a' }, - { 't':'s' }, - { 't':'d' }, - { 't':'f','n': True }, - { 't':'g' }, - { 't':'h' }, - { 't':'j','n': True }, - { 't':'k' }, - { 't':'l' }, - {'t':';\n:','fs':5}, - { 't':'\'\n@','fs':5 } + { 't':'↹','fs':12 }, + { 't':'a' }, + { 't':'s' }, + { 't':'d' }, + { 't':'f','n': True }, + { 't':'g' }, + { 't':'h' }, + { 't':'j','n': True }, + { 't':'k' }, + { 't':'l' }, + {'t':';\n:','fs':5}, + { 't':'\'\n@','fs':5 } ], 2: [ - { 't':'⇧','fs':12 }, - { 't':'z' }, - { 't':'x' }, - { 't':'c' }, - { 't':'v' }, - { 't':'b' }, - { 't':'n' }, - { 't':'m' }, - { 't':',\n<','fs':5 }, - { 't':'.\n>','fs':5 }, - { 't':'/\n?','fs':5 }, - { 't':'⊼','fs':5 } + { 't':'⇧','fs':12 }, + { 't':'z' }, + { 't':'x' }, + { 't':'c' }, + { 't':'v' }, + { 't':'b' }, + { 't':'n' }, + { 't':'m' }, + { 't':',\n<','fs':5 }, + { 't':'.\n>','fs':5 }, + { 't':'/\n?','fs':5 }, + { 't':'⊼','fs':5 } ], 3: [ - { 't':'⎈','f':"DejaVu Sans Mono",'fs':12 }, - { 't':'','f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf" }, - { 't':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, - { 't':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, - { 't':'⇓','f':"DejaVu Sans Mono",'fs':12}, - {'w':2,'convex':True}, - { 't':'⇑','f':"DejaVu Sans Mono",'fs':12 }, - { 't':'←','fs':12 }, - { 't':'↑','fs':12 }, - { 't':'↓','fs':12 }, - { 't':'→','fs':12 } - + { 't':'Fn','fs':6 }, + { 't':'⎈','f':"DejaVu Sans Mono",'fs':12 }, + { 't':'','f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf" }, + { 't':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, + { 't':'⇓','f':"DejaVu Sans Mono",'fs':12}, + { 'w':2,'convex':True}, + { 't':'⇑','f':"DejaVu Sans Mono",'fs':12 }, + { 't':'←','fs':12 }, + { 't':'↑','fs':12 }, + { 't':'↓','fs':12 }, + { 't':'→','fs':12 } ], } diff --git a/examples/preonic.py b/examples/preonic.py index 8d0953a..62f2597 100644 --- a/examples/preonic.py +++ b/examples/preonic.py @@ -1,60 +1,107 @@ -#!/usr/bin/env python3 - +import opk import cadquery as cq -from opk import * +from cadquery import exporters +from kb_render import * + +keys = { + 0: [ + { 't':'⎋','fs':12}, + { 't':'!\n1','fs':5}, + { 't':'"\n2','fs':5}, + { 't':'£\n3','fs':5}, + { 't':'$\n4','fs':5}, + { 't':'%\n5','fs':5}, + { 't':'^\n6','fs':5}, + { 't':'&\n7','fs':5}, + { 't':'*\n8','fs':5}, + { 't':'(\n9','fs':5}, + { 't':')\n0','fs':5}, + { 't':'⌫','fs':12} + ], + 1: [ + { 't':'q' }, + { 't':'w' }, + { 't':'e' }, + { 't':'r' }, + { 't':'t' }, + { 't':'y' }, + { 't':'u' }, + { 't':'i' }, + { 't':'o' }, + { 't':'p' }, + { 't':'{\n[','fs':5}, + { 't':'}\n]','fs':5}, + ], + 2: [ + { 't':'↹','fs':12 }, + { 't':'a' }, + { 't':'s' }, + { 't':'d' }, + { 't':'f','n': True }, + { 't':'g' }, + { 't':'h' }, + { 't':'j','n': True }, + { 't':'k' }, + { 't':'l' }, + {'t':';\n:','fs':5}, + { 't':'\'\n@','fs':5 } + ], + 3: [ + { 't':'⇧','fs':12 }, + { 't':'z' }, + { 't':'x' }, + { 't':'c' }, + { 't':'v' }, + { 't':'b' }, + { 't':'n' }, + { 't':'m' }, + { 't':',\n<','fs':5 }, + { 't':'.\n>','fs':5 }, + { 't':'/\n?','fs':5 }, + { 't':'⊼','fs':5 } + ], + 4: [ + { 't':'Fn','fs':6 }, + { 't':'⎈','f':"DejaVu Sans Mono",'fs':12 }, + { 't':'','f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf" }, + { 't':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, + { 't':'⇓','f':"DejaVu Sans Mono",'fs':12}, + { 'w':2,'convex':True}, + { 't':'⇑','f':"DejaVu Sans Mono",'fs':12 }, + { 't':'←','fs':12 }, + { 't':'↑','fs':12 }, + { 't':'↓','fs':12 }, + { 't':'→','fs':12 } + ], +} + +#rows = [ + #{'angle': 13, 'height': 16, 'keys': keys[0] }, # row 0, function row + #{'angle': 9, 'height': 14, 'keys': keys[1] }, # row 1, numbers row + #{'angle': 8, 'height': 12, 'keys': keys[2] }, # row 2, QWERT + #{'angle': -6, 'height': 11.5, 'keys': keys[3] }, # row 3, ASDFG + #{'angle': -8, 'height': 13, 'keys': keys[4] }, # row 4, ZXCVB + #{'angle': 0, 'height': 12.5, 'keys': keys[5] }, # row 5, bottom row +#] +rows = [ + {'angle': 9, 'height': 14, 'keys': keys[0] }, # row 1, numbers row + {'angle': 8, 'height': 12, 'keys': keys[1] }, # row 2, QWERT + {'angle': -6, 'height': 11.5, 'keys': keys[2] }, # row 3, ASDFG + {'angle': -8, 'height': 13, 'keys': keys[3] }, # row 4, ZXCVB + {'angle': 0, 'height': 12.5, 'keys': keys[4] }, # row 5, bottom row +] + +mainFont = "DejaVu Sans Mono" +mainSize = 9 + +sx = 19.05 +sy = 19.05 + +assy = render_kb(rows, mainFont=mainFont, mainSize = mainSize, sx = sx, sy = sy) -leg = [ - ["⎋","!\n1","\"\n2","£\n3","$\n4","%\n5","^\n6","&\n7","*\n8","(\n9",")\n0","⌫"], - ["q", "w", "e","r","t","y","u","i","o","p","{\n[","}\n]"], - ["↹","a","s","d","f","g","h","j","k","l",":\n;","@\n'"], - ["⇧","z","x","c","v","b","n","m","<\n,",">\n.","?\n/","⌅"], - ["⎈","⎇","","⎇","⇓","","⇑","←","↓","↑","→"]] -lay= [[1]*12, [1]*12,[1]*12,[1]*12,[1]*5+[2]+[1]*5] -fonts=[ - ["DejaVu Sans Mono"]*12, - ["DejaVu Sans Mono"]*12, - ["DejaVu Sans Mono"]*12, - ["DejaVu Sans Mono"]*12, - ["DejaVu Sans Mono"]*11 - ] -sx=19.05 -sy=19.05 -pre = cq.Assembly() -y = 0 -i = -1 -j = -1 -angles=[9,8.5,-6,-7,0] -vfs=[0,9,7,6,4.5,4.5] +if 'show_object' in locals(): + show_object(assy) -for row,ll,ff in zip(leg,lay,fonts): - y -= sy - i += 1 - x = 0 - for k,l,f in zip(row,ll,ff): - print(k,l) - w = l*sx/2.0 - j += 1 - x += w - convex=False - if k == '': - convex=True - scoop = 2.5 - if k in ['f','F','j','J']: - scoop = 2.5*1.2 - fs=3 - if len(k)<=5: - fs=vfs[len(k)] - if (len(k.split("\n"))==2): - fs = 4.5 - pre.add(keycap(legend=k, - angle=angles[i], - font=f, - depth = scoop, - convex=convex, - fontsize =fs, - unitX=l), - name="k{}{}".format(i,j), - loc=cq.Location(cq.Vector(x,y,0))) - x += w -#cq.exporters.export(pre.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) -show_object(pre, name="pre", options={"alpha": 0}) +# Export the whole assembly, very handy especially for STEP +#exporters.export(assy.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +#exporters.export(assy.toCompound(), 'keycaps.step') diff --git a/examples/tkl.py b/examples/tkl.py index ccd5ad2..28ab8e0 100644 --- a/examples/tkl.py +++ b/examples/tkl.py @@ -1,89 +1,130 @@ -#!/usr/bin/env python3 - +import opk import cadquery as cq -from opk import * -leg = [ ["⎋","F1","F2","F3","F4","F5","F6","F7","F8","F9","F10","F11","F12", "Prn\nScr","Scr\nLock","Pause"], - ["¬\n`","!\n1","\"\n2","£\n3","$\n4","%\n5","^\n6","&\n7","*\n8","(\n9",")\n0","_\n-","=\n+","⌫","Ins","Home","Pg\nUp"], -["↹","q","w","e","r","t","y","u","i","o","p","[","]","|\n\\","Del","End","Pg\nDn"], -["⇪","a","s","d","f","g","h","j","k","l",";","'","enter"], -["⇧","z","x","c","v","b","n","m",",",".","/","⇧","↑"], -["⎈","","Alt","","Alt","Fn","Mnu","Ctrl","←","↓","→"]] -lay= [[1]*16, - [1]*13+[2]+[1]*3, - [1.5]+[1]*12+[1.5]+[1]*3, - [1.75]+[1]*11+[2.25], - [2.25]+[1]*10+[2.75]+[1], - [1.25,1.25,1.25,6.25,1.25,1.25,1.25,1.25,1,1,1]] -fonts=[ - ["DejaVu Sans Mono"]*16, - ["DejaVu Sans Mono"]*17, - ["DejaVu Sans Mono"]*17, - ["DejaVu Sans Mono"]*13, - ["DejaVu Sans Mono"]*13, - ["DejaVu Sans Mono"]+["Font Awesome 6 Brands"]+["Noto Sans"]*6+["DejaVu Sans Mono"]*3 - ] - -offx=[[0,1,0,0,0]+[0.5,0,0,0]+[0.5,0,0,0]+[0.5,0,0], - [0.0]*14+[0.5,0,0], - [0.0]*14+[0.5,0,0], - [0.0]*13, - [0.0]*12+[1.5], - [0.0]*8+[0.5,0,0] - ] -offy=[[0.5]+[0]*15, - [0]*17, - [0]*17, - [0]*13, - [0]*13, - [0]*11 - ] - -sx=19.05 -sy=19.05 +from cadquery import exporters +from kb_render import * -m65 = cq.Assembly() +keys = { + 0: [ + {'t':'⎋'}, + {'t':'F1','ox':1.0,'fs':6}, + {'t':'F2','fs':6}, + {'t':'F3','fs':6}, + {'t':'F4','fs':6}, + {'t':'F5','ox':0.5,'fs':6}, + {'t':'F6','fs':6}, + {'t':'F7','fs':6}, + {'t':'F8','fs':6}, + {'t':'F9','ox':0.5,'fs':6}, + {'t':'F10','fs':6}, + {'t':'F11','fs':6}, + {'t':'F12','fs':6}, + {'t':'Print\nScreen','ox':0.5,'fs':3}, + {'t':'Screen\nLock','fs':3}, + {'t':'Pause','fs':3} + ], + 1: [ + { 't':'`\n¬','fs':5, 'oy':-0.5}, + { 't':'!\n1','fs':5}, + { 't':'"\n2','fs':5}, + { 't':'£\n3','fs':5}, + { 't':'$\n4','fs':5}, + { 't':'%\n5','fs':5}, + { 't':'^\n6','fs':5}, + { 't':'&\n7','fs':5}, + { 't':'*\n8','fs':5}, + { 't':'(\n9','fs':5}, + { 't':')\n0','fs':5}, + { 't':'-\n_','fs':5}, + { 't':'+\n=','fs':5}, + { 'w':2,'t':'⌫','fs':12}, + { 't':'Ins','fs':5,'ox':0.5}, + { 't':'Home','fs':5}, + { 't':'Pg\nUp','fs':5}, + ], + 2: [ + { 'w':1.5,'t':'↹','fs':12,'oy':-0.5}, + { 't':'q' }, + { 't':'w' }, + { 't':'e' }, + { 't':'r' }, + { 't':'t' }, + { 't':'y' }, + { 't':'u' }, + { 't':'i' }, + { 't':'o' }, + { 't':'p' }, + { 't':'[\n{','fs':5 }, + { 't':']\n}','fs':5 }, + { 'w':1.5,'t':'|\n\\','fs':5 }, + { 't':'Del','ox':0.5,'fs':5 }, + { 't':'End','fs':5 }, + { 't':'Pg\nDn','fs':5} + ], + 3: [ + { 'w':1.75,'t':'⇪','oy':-0.5 }, + { 't':'a' }, + { 't':'s' }, + { 't':'d' }, + { 't':'f','n': True }, + { 't':'g' }, + { 't':'h' }, + { 't':'j','n': True }, + { 't':'k' }, + { 't':'l' }, + { 't':';\n:','fs':5 }, + { 't':'\'\n@','fs':5 }, + { 'w':2.25,'t':'⊼','fs':12 } + ], + 4: [ + { 'w':2.25,'t':'⇧','oy':-0.5 }, + { 't':'z' }, + { 't':'x' }, + { 't':'c' }, + { 't':'v' }, + { 't':'b' }, + { 't':'n' }, + { 't':'m' }, + { 't':',\n<','fs':5 }, + { 't':'.\n>','fs':5 }, + { 't':'/\n?','fs':5 }, + { 'w':2.75,'t':'⇧'}, + { 't':'↑','fs':12,'ox':1.5} + ], + 5: [ + { 'w': 1.25,'t':'⎈','oy':-0.5 }, + { 'w': 1.25,'t':'','f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf"}, + { 'w': 1.25,'t':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf"}, + { 'w': 6.25, 'convex':True}, + { 'w':1.25, 't':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, + {'w':1.25, 't':'Fn'}, + { 'w': 1.25,'t':'','f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf" }, + { 'w': 1.25,'t':'⎈'}, + { 't':'←','fs':12,'ox': 0.5}, + { 't':'↓','fs':12 }, + { 't':'→','fs':12 } + ], +} -y = 0 -i = -1 -j = -1 +rows = [ + {'angle': 13, 'height': 16, 'keys': keys[0] }, # row 0, function row + {'angle': 9, 'height': 14, 'keys': keys[1] }, # row 1, numbers row + {'angle': 8, 'height': 12, 'keys': keys[2] }, # row 2, QWERT + {'angle': -6, 'height': 11.5, 'keys': keys[3] }, # row 3, ASDFG + {'angle': -8, 'height': 13, 'keys': keys[4] }, # row 4, ZXCVB + {'angle': 0, 'height': 12.5, 'keys': keys[5] }, # row 5, bottom row +] -angles=[13.5,9,8.5,-6,-7,0] -vfs=[0,9,7,6,4.5,4.5] +mainFont = "DejaVu Sans Mono" +mainSize = 9 -for row,ll,ff,ofx,ofy in zip(leg,lay,fonts,offx,offy): +sx = 19.05 +sy = 19.05 - i += 1 - y = -(i+1)*sy +assy = render_kb(rows, mainFont=mainFont, mainSize = mainSize, sx = sx, sy = sy) - x = 0 - for k,l,f,ox,oy in zip(row,ll,ff,ofx,ofy): - print(k,len(k),l,f) - w = l*sx/2.0 - j += 1 - x += w + ox*sx - y += oy*sy - convex=False - if k == '': - convex=True - scoop = 2.5 - if k in ['f','F','j','J']: - scoop = 2.5*1.2 - fs=3 - if len(k)<=5: - fs=vfs[len(k)] - if (len(k.split("\n"))==2): - fs = 4.5 - m65.add(keycap(legend=k, - angle=angles[i], - font=f, - convex=convex, - depth = scoop, - fontsize=fs, - unitX=l), - name="k{}{}".format(i,j), - loc=cq.Location(cq.Vector(x,y,0)) - ) - x += w +if 'show_object' in locals(): + show_object(assy) -#cq.exporters.export(m65.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) -show_object(m65, name="legend", options={'color': 'red', 'alpha': 0}) +# Export the whole assembly, very handy especially for STEP +#exporters.export(assy.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +#exporters.export(assy.toCompound(), 'keycaps.step') From 57a7fb405cd0b6b01d3c87c732289b1bec25d6b0 Mon Sep 17 00:00:00 2001 From: Alin Marin Elena Date: Mon, 12 Sep 2022 07:26:27 +0100 Subject: [PATCH 41/44] update --- examples/m65.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/m65.py b/examples/m65.py index e9c7a89..0072325 100644 --- a/examples/m65.py +++ b/examples/m65.py @@ -5,7 +5,7 @@ keys = { 0: [ - { 't':'⎋ `\n ¬','fs':5}, + { 't':'⎋ `\n ¬','fs':5, 'f':"DejaVu Sans Mono"}, { 't':'1\n!','fs':5 }, { 't':'2\n\"','fs':5 }, { 't':'3\n£','fs':5 }, @@ -37,11 +37,11 @@ 2: [ { 't':'# \n~ ⇪','fs':5 }, { 't':'a α\nă ̇','fs':5 }, - { 't':'s ∑\nș ⨋','fs':5 }, + { 't':'s ∑\nș ⨋','fs':5, 'f':"/usr/share/fonts/truetype/NotoSansMath-Regular.ttf" }, { 't':'d δ\n ∂','fs':5 }, { 't':'f φ\n ψ','n': True,'fs':5 }, { 't':'g γ\n Γ','fs':5 }, - { 't':'h ℏ\n 𝓗','fs':5 }, + { 't':'h ℏ\n 𝓗','fs':5, 'f':"/usr/share/fonts/truetype/NotoSansMath-Regular.ttf" }, { 't':'j ∈\n ∉','n': True,'fs':5 }, { 't':'k ϰ\n ∆','fs':5 }, { 't':'l λ\n Λ','fs':5 }, @@ -97,7 +97,7 @@ {'angle': 0, 'height': 12.5, 'keys': keys[4] }, # row 5, bottom row ] -mainFont = "DejaVu Sans Mono" +mainFont = "./Atkinson-Hyperlegible-Bold-102.otf" mainSize = 9 sx = 19.05 @@ -109,5 +109,5 @@ show_object(assy) # Export the whole assembly, very handy especially for STEP -#exporters.export(assy.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +exporters.export(assy.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) #exporters.export(assy.toCompound(), 'keycaps.step') From d12312aafe4a03eb9f977e50ca78dd2e08c03ca3 Mon Sep 17 00:00:00 2001 From: Alin Marin Elena Date: Sat, 24 Sep 2022 07:23:15 +0100 Subject: [PATCH 42/44] update --- pyproject.toml | 2 +- src/opk.py | 170 ++++++++++++++++++++++++++++--------------------- 2 files changed, 100 insertions(+), 72 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 06cdd37..4565c3e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "opk" -version = "0.0.14" +version = "0.0.16" authors = [ { name="Matteo (Matt3o) Spinelli", email="info@matt3o.com" }, ] diff --git a/src/opk.py b/src/opk.py index bf295b9..a814e60 100644 --- a/src/opk.py +++ b/src/opk.py @@ -1,12 +1,12 @@ """ ========================== - ██████ ██████ ██ ██ - ██ ██ ██ ██ ██ ██ - ██ ██ ██████ █████ - ██ ██ ██ ██ ██ - ██████ ██ ██ ██ + ██████ ██████ ██ ██ + ██ ██ ██ ██ ██ ██ + ██ ██ ██████ █████ + ██ ██ ██ ██ ██ + ██████ ██ ██ ██ ========================== - Open Programmatic Keycap + Open Programmatic Keycap ========================== OPK is a spherical top keycap profile developed in CadQuery @@ -23,6 +23,7 @@ https://matt3o.com """ +import math import cadquery as cq def keycap( @@ -40,8 +41,9 @@ def keycap( 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 + font: str = "sans-serif", # font name, use a font name including extension to use a local file + fontsize: float = 10, # the font size is in units + pos: bool = False # use POS style stabilizers ): top_diff = base - top @@ -55,7 +57,10 @@ 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 + + if unitX < 2 and unitY < 2: + pos = False # Three-section loft of rounded rectangles. Can't find a better way to do variable fillet base = ( @@ -96,7 +101,7 @@ def keycap( # Create a body that will be carved from the main shape to create the top scoop if convex: - tool = ( + scoop = ( cq.Workplane("YZ").transformed(offset=cq.Vector(0, height-2.1, -bx/2), rotate=cq.Vector(0, 0, angle)) .moveTo(-by/2, -1) .threePointArc((0, 2), (by/2, -1)) @@ -106,7 +111,7 @@ def keycap( .extrude(bx, combine=False) ) else: - tool = ( + scoop = ( cq.Workplane("YZ").transformed(offset=cq.Vector(0, height, bx/2), rotate=cq.Vector(0, 0, angle)) .moveTo(-by/2+2,0) .threePointArc((0, min(-0.1, -depth+1.5)), (by/2-2, 0)) @@ -129,7 +134,7 @@ def keycap( ) #show_object(tool, options={'alpha': 0.4}) - keycap = keycap - tool + keycap = keycap - scoop # Top edge fillet keycap = keycap.edges(">Z").fillet(0.6) @@ -139,44 +144,60 @@ def keycap( shell = ( cq.Workplane("XY").rect(bx-thickness*2, by-thickness*2) .workplane(offset=height/4).rect(bx-thickness*3, by-thickness*3) - .workplane().transformed(offset=cq.Vector(0, 0, height-height/4-4.5), rotate=cq.Vector(angle, 0, 0)).rect(tx-thickness*2, ty-thickness*2) + .workplane().transformed(offset=cq.Vector(0, 0, height-height/4-4.5), rotate=cq.Vector(angle, 0, 0)).rect(tx-thickness*2+.5, ty-thickness*2+.5) .loft() ) keycap = keycap - shell + # create a temporary surface that will be used to project the stems to + # this is needed because extrude(face) needs the entire extruded outline to be contained inside the destination face + tmpface = shell.faces('>Z').workplane().rect(bx*2, by*2).val() + tmpface = cq.Face.makeFromWires(tmpface) + # Build the stem and the keycap guts - # TODO: we need to find a better way to build the stem... - if ( unitY > unitX ): - if unitY > 1.75: - dist = 2.25 / 2 * 19.05 - 19.05 / 2 - stem_pts = [(0, 0), (0, dist), (0, -dist)] - stem1 = ( - cq.Sketch() - .rect(0.8, ty) - .push(stem_pts) - .rect(tx, 0.8) - .circle(2.75) - .clean() - ) - else: - if unitX < 2: - stem_pts = [(0,0)] - elif unitX < 3: # keycaps smaller than 3unit all have 2.25 stabilizers - dist = 2.25 / 2 * 19.05 - 19.05 / 2 - stem_pts = [(0,0), (dist, 0), (-dist,0)] + + if pos: # POS-like stems + stem_pts = [] + ribh_pts = [] + ribv_pts = [] + + stem_num_x = math.floor(unitX) + stem_num_y = math.floor(unitY) + stem_start_x = round(-19.05 * (stem_num_x / 2) + 19.05 / 2, 6) + stem_start_y = round(-19.05 * (stem_num_y / 2) + 19.05 / 2, 6) + + for i in range(0, stem_num_y): + ribh_pts.extend([(0, stem_start_y+i*19.05)]) + for l in range(0, stem_num_x): + if i == 0: + ribv_pts.extend([(stem_start_x+l*19.05, 0)]) + stem_pts.extend([(stem_start_x+l*19.05, stem_start_y+i*19.05)]) + + else: # standard stems + stem_pts = [(0,0)] + + if ( unitY > unitX ): + if unitY > 2.75: + dist = unitY / 2 * 19.05 - 19.05 / 2 + stem_pts.extend([(0, dist), (0, -dist)]) + elif unitY > 1.75: + dist = 2.25 / 2 * 19.05 - 19.05 / 2 + stem_pts.extend([(0, -dist), (0, dist)]) + + ribh_pts = stem_pts + ribv_pts = [(0,0)] else: - dist = unitX / 2 * 19.05 - 19.05 / 2 - stem_pts = [(0,0), (dist, 0), (-dist,0)] - - stem1 = ( - cq.Sketch() - .rect(tx, 0.8) - .push(stem_pts) - .rect(0.8, ty) - .circle(2.75) - .clean() - ) - + if unitX > 2.75: + dist = unitX / 2 * 19.05 - 19.05 / 2 + stem_pts.extend([(dist, 0), (-dist,0)]) + elif unitX > 1.75: # keycaps smaller than 3unit all have 2.25 stabilizers + dist = 2.25 / 2 * 19.05 - 19.05 / 2 + stem_pts.extend([(dist, 0), (-dist,0)]) + + ribh_pts = [(0,0)] + ribv_pts = stem_pts + + # this is the stem + stem2 = ( cq.Sketch() .push(stem_pts) @@ -186,9 +207,16 @@ def keycap( ) keycap = ( - keycap.faces(" Date: Sat, 10 Dec 2022 09:15:46 +0000 Subject: [PATCH 43/44] update --- examples/generate_exports.py | 80 ++++++++++++++++++++++++++++++++++++ examples/m65.py | 8 ++-- examples/numpad.py | 4 +- examples/tkl.py | 4 +- 4 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 examples/generate_exports.py diff --git a/examples/generate_exports.py b/examples/generate_exports.py new file mode 100644 index 0000000..f08b177 --- /dev/null +++ b/examples/generate_exports.py @@ -0,0 +1,80 @@ +import opk +import cadquery as cq +from cadquery import exporters + +keys = { + 0: [ + { 'unitX': 1 }, + ], + 1: [ + { 'unitX': 1 }, + { 'unitX': 2 } + ], + 2: [ + { 'unitX': 1 }, + { 'unitX': 1.5 } + ], + 3: [ + { 'unitX': 1 }, + { 'unitX': 1, 'depth': 3.6 }, + { 'unitX': 1.75 }, + { 'unitX': 2.25 } + ], + 4: [ + { 'unitX': 1 }, + { 'unitX': 1.25 }, + { 'unitX': 1.75 }, + { 'unitX': 2.25 }, + { 'unitX': 2.75 }, + ], + 5: [ + { 'unitX': 1 }, + { 'unitX': 1.25 }, + { 'unitX': 1.5 }, + { 'unitX': 6.25, 'convex': True } + ] +} + +rows = [ + {'angle': 13, 'height': 16, 'keys': keys[0] }, # row 0, function row + {'angle': 9, 'height': 14, 'keys': keys[1] }, # row 1, numbers row + {'angle': 8, 'height': 12, 'keys': keys[2] }, # row 2, QWERT + {'angle': -6, 'height': 11.5, 'keys': keys[3] }, # row 3, ASDFG + {'angle': -8, 'height': 13, 'keys': keys[4] }, # row 4, ZXCVB + {'angle': 0, 'height': 12.5, 'keys': keys[5] }, # row 5, bottom row +] + +assy = cq.Assembly() + +y = 0 +for i, r in enumerate(rows): + x = 0 + for k in r['keys']: + name = "row{}_U{}".format(i,k['unitX']) + convex = False + if 'convex' in k: + convex = k['convex'] + name+= "_space" + + depth = 2.8 + if 'depth' in k: + if k['depth'] > depth: name+= "_homing" + depth = k['depth'] + + print("Generating: ", name) + cap = opk.keycap(angle=r['angle'], height=r['height'], unitX=k['unitX'], convex=convex, depth=depth) + # Export one key at the time + #exporters.export(cap, './export/STEP/' + name + '.step') + #exporters.export(cap, './export/STL/' + name + '.stl', tolerance=0.001, angularTolerance=0.05) + w = 19.05 * k['unitX'] / 2 + x+= w + assy.add(cap, name=name, loc=cq.Location(cq.Vector(x,y,0))) + x+= w + y -= 19.05 + +if 'show_object' in locals(): + show_object(assy) + +# Export the whole assembly, very handy especially for STEP +#exporters.export(assy.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) +#exporters.export(assy.toCompound(), 'keycaps.step') diff --git a/examples/m65.py b/examples/m65.py index 0072325..d3f392c 100644 --- a/examples/m65.py +++ b/examples/m65.py @@ -70,9 +70,9 @@ { 't':'⇓','f':"DejaVu Sans Mono",'fs':12}, { 't':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, { 't':'⇑','f':"DejaVu Sans Mono",'fs':12 }, - { 'convex':True}, + { 't':'','convex':True,'f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf",'fs':9}, { 't':'','convex':True,'f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf",'fs':9}, - { 'convex':True}, + { 't':'','convex':True,'f':"/usr/share/fonts/texlive-fontawesome5/FontAwesome5Brands-Regular-400.otf",'fs':9}, { 't':'⎇','f':"/usr/share/fonts/truetype/NotoSansSymbols-Black.ttf" }, { 't':'⇧','fs':12 }, { 't':'←','fs':12 }, @@ -109,5 +109,5 @@ show_object(assy) # Export the whole assembly, very handy especially for STEP -exporters.export(assy.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) -#exporters.export(assy.toCompound(), 'keycaps.step') +exporters.export(assy.toCompound(), 'm65.stl', tolerance=0.001, angularTolerance=0.05) +exporters.export(assy.toCompound(), 'm65.step') diff --git a/examples/numpad.py b/examples/numpad.py index 5e3a5f4..742643f 100644 --- a/examples/numpad.py +++ b/examples/numpad.py @@ -61,5 +61,5 @@ show_object(assy) # Export the whole assembly, very handy especially for STEP -#exporters.export(assy.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) -#exporters.export(assy.toCompound(), 'keycaps.step') +exporters.export(assy.toCompound(), 'numpad.stl', tolerance=0.001, angularTolerance=0.05) +exporters.export(assy.toCompound(), 'numpad.step') diff --git a/examples/tkl.py b/examples/tkl.py index 28ab8e0..4c177a5 100644 --- a/examples/tkl.py +++ b/examples/tkl.py @@ -126,5 +126,5 @@ show_object(assy) # Export the whole assembly, very handy especially for STEP -#exporters.export(assy.toCompound(), 'keycaps.stl', tolerance=0.001, angularTolerance=0.05) -#exporters.export(assy.toCompound(), 'keycaps.step') +exporters.export(assy.toCompound(), 'tkl.stl', tolerance=0.001, angularTolerance=0.05) +exporters.export(assy.toCompound(), 'tkl.step') From d416075d7cd7231f7f0e72a80ee8f200c234711f Mon Sep 17 00:00:00 2001 From: Alin Marin Elena Date: Sat, 10 Dec 2022 09:17:45 +0000 Subject: [PATCH 44/44] update --- examples/test.scad | 1 - 1 file changed, 1 deletion(-) delete mode 100644 examples/test.scad diff --git a/examples/test.scad b/examples/test.scad deleted file mode 100644 index 9967a48..0000000 --- a/examples/test.scad +++ /dev/null @@ -1 +0,0 @@ -import("k0-1.stl");