From 1866c114f144b6fc828716b00782ec8d516c3e41 Mon Sep 17 00:00:00 2001 From: joshreve Date: Sat, 3 Jul 2021 13:01:28 -0400 Subject: Large change to things directory with pregenerated files in multiple configurations. Implemented prebuild generator to automatically create pregenerated files. --- src/dactyl_manuform.py | 257 ++++----------------------------- src/dactyl_manuform_cadquery.py | 311 +++++++++++----------------------------- src/generate_configuration.py | 229 +++++++++++++++++++++++++++++ src/model_builder.py | 134 +++++++++++++++++ src/run.py | 15 ++ src/run_config.json | 202 ++++++++++++++++++++++++++ 6 files changed, 694 insertions(+), 454 deletions(-) create mode 100644 src/generate_configuration.py create mode 100644 src/model_builder.py create mode 100644 src/run.py create mode 100644 src/run_config.json (limited to 'src') diff --git a/src/dactyl_manuform.py b/src/dactyl_manuform.py index bc6e309..4e58bd4 100644 --- a/src/dactyl_manuform.py +++ b/src/dactyl_manuform.py @@ -2,7 +2,8 @@ import solid as sl import numpy as np from numpy import pi import os.path as path - +import os +import json def deg2rad(degrees: float) -> float: return degrees * pi / 180 @@ -12,232 +13,24 @@ def rad2deg(rad: float) -> float: return rad * 180 / pi -#################################### -## START CONFIGURATION SECTION -#################################### - -###################### -## Run parameters ## -###################### - -debug_exports = False - -###################### -## Shape parameters ## -###################### - -show_caps = False +with open('run_config.json', mode='r') as fid: + data = json.load(fid) +for item in data: + locals()[item] = data[item] -nrows = 5 # key rows -ncols = 6 # key columns - -alpha = pi / 12.0 # curvature of the columns -beta = pi / 36.0 # curvature of the rows -centerrow = nrows - 3 # controls front_back tilt -centercol = 3 # controls left_right tilt / tenting (higher number is more tenting) -tenting_angle = pi / 12.0 # or, change this for more precise tenting control - -# symmetry states if it is a symmetric or asymmetric build. If asymmetric it doubles the generation time. -symmetry = "symmetric" # "asymmetric" or "symmetric" +if oled_mount_type is not None: + for item in oled_configurations[oled_mount_type]: + locals()[item] = oled_configurations[oled_mount_type][item] if nrows > 5: - column_style = "orthographic" -else: - column_style = "standard" # options include :standard, :orthographic, and :fixed + column_style = column_style_gt5 -thumb_offsets = [6, -3, 7] -keyboard_z_offset = ( - 9 # controls overall height# original=9 with centercol=3# use 16 for centercol=2 -) - -extra_width = 2.5 # extra space between the base of keys# original= 2 -extra_height = 1.0 # original= 0.5 - -wall_z_offset = 15 # length of the first downward_sloping part of the wall -wall_x_offset = 5 # offset in the x and/or y direction for the first downward_sloping part of the wall (negative) -wall_y_offset = 6 # offset in the x and/or y direction for the first downward_sloping part of the wall (negative) -left_wall_x_offset = 12 # specific values for the left side due to the minimal wall. -left_wall_z_offset = 3 # specific values for the left side due to the minimal wall. -wall_thickness = 4.5 # wall thickness parameter used on upper/mid stage of the wall -wall_base_y_thickness = 4.5 # wall thickness at the lower stage -wall_base_x_thickness = 4.5 # wall thickness at the lower stage - -wall_base_back_thickness = 4.5 # wall thickness at the lower stage in the specifically in back for interface. - -## Settings for column_style == :fixed -## The defaults roughly match Maltron settings -## http://patentimages.storage.googleapis.com/EP0219944A2/imgf0002.png -## fixed_z overrides the z portion of the column ofsets above. -## NOTE: THIS DOESN'T WORK QUITE LIKE I'D HOPED. -fixed_angles = [deg2rad(10), deg2rad(10), 0, 0, 0, deg2rad(-15), deg2rad(-15)] -fixed_x = [-41.5, -22.5, 0, 20.3, 41.4, 65.5, 89.6] # relative to the middle finger -fixed_z = [12.1, 8.3, 0, 5, 10.7, 14.5, 17.5] -fixed_tenting = deg2rad(0) - -####################### -## General variables ## -####################### +centerrow = nrows - centerrow_offset lastrow = nrows - 1 cornerrow = lastrow - 1 lastcol = ncols - 1 -################# -## Switch Hole ## -################# - -# plate options are -# 'HOLE' = a square hole. Also useful for applying custom plate files. -# 'NUB' = original side nubs. -# 'UNDERCUT' = snap fit undercut. May require CLIP_THICKNESS and possibly CLIP_UNDERCUT tweaking -# and/or filing to get proper snap. -# 'HS_NUB' = hot swap underside with nubs. -# 'HS_UNDERCUT' = hot swap underside with undercut. Does not generate properly. Hot swap step needs to be modified. -plate_style = 'UNDERCUT' - -hole_keyswitch_height = 14.0 -hole_keyswitch_width = 14.0 - -nub_keyswitch_height = 14.4 -nub_keyswitch_width = 14.4 - -undercut_keyswitch_height = 14.4 -undercut_keyswitch_width = 14.4 - -sa_profile_key_height = 12.7 -plate_thickness = 4 - -# Undercut style dimensions -clip_thickness = 1.4 -clip_undercut = 1.0 -undercut_transition = .2 # NOT FUNCTIONAL WITH OPENSCAD, ONLY WORKS WITH CADQUERY - -# Custom plate step file -plate_file = None -plate_offset = 0.0 - -########################## -## OLED Mount Location -########################## -# Initial pass will be manual placement. Can be used to create other mounts as well. -# Mount type options: -# None or 'NONE' = No OLED mount -# 'UNDERCUT' = Simple rectangle with undercut for clip in item -# 'SLIDING' = Features to slide the OLED in place and use a pin or block to secure from underneath. -# 'CLIP' = Features to set the OLED in a frame a snap a bezel down to hold it in place. - -oled_mount_type = 'CLIP' - -if oled_mount_type == 'UNDERCUT': - # Common parameters - oled_mount_width = 15.0 - oled_mount_height = 35.0 - oled_mount_rim = 3.0 - oled_mount_depth = 6.0 - oled_mount_cut_depth = 20.0 - oled_mount_location_xyz = (-80.0, 20.0, 45.0) - oled_mount_rotation_xyz = (13.0, 0.0, -6.0) - oled_left_wall_x_offset_override = 28.0 - oled_left_wall_z_offset_override = 0.0 - - # 'UNDERCUT' Parameters - oled_mount_undercut = 1.0 - oled_mount_undercut_thickness = 2.0 - -elif oled_mount_type == 'SLIDING': - # Common parameters - oled_mount_width = 12.5 # width of OLED, plus clearance - oled_mount_height = 25.0 # length of screen - oled_mount_rim = 2.5 - oled_mount_depth = 8.0 - oled_mount_cut_depth = 20.0 - oled_mount_location_xyz = (-78.0, 10.0, 41.0) - oled_mount_rotation_xyz = (6.0, 0.0, -3.0) - oled_left_wall_x_offset_override = 24.0 - oled_left_wall_z_offset_override = 0.0 - - # 'SLIDING' Parameters - oled_thickness = 4.2 # thickness of OLED, plus clearance. Must include components - oled_edge_overlap_end = 6.5 # length from end of viewable screen to end of PCB - oled_edge_overlap_connector = 5.5 # length from end of viewable screen to end of PCB on connection side. - oled_edge_overlap_thickness = 2.5 # thickness of material over edge of PCB - oled_edge_overlap_clearance = 2.5 # Clearance to insert PCB before laying down and sliding. - oled_edge_chamfer = 2.0 - -elif oled_mount_type == 'CLIP': - # Common parameters - oled_mount_width = 12.5 # whole OLED width - oled_mount_height = 39.0 # whole OLED length - oled_mount_rim = 2.0 - oled_mount_depth = 7.0 - oled_mount_cut_depth = 20.0 - oled_mount_location_xyz = (-78.0, 20.0, 42.0) - oled_mount_rotation_xyz = (12.0, 0.0, -6.0) - oled_left_wall_x_offset_override = 24.0 - oled_left_wall_z_offset_override = 0.0 - - # 'CLIP' Parameters - oled_thickness = 4.2 # thickness of OLED, plus clearance. Must include components - oled_mount_bezel_thickness = 3.5 # z thickness of clip bezel - oled_mount_bezel_chamfer = 2.0 # depth of the 45 degree chamfer - oled_mount_connector_hole = 6.0 - oled_screen_start_from_conn_end = 6.5 - oled_screen_length = 24.5 - oled_screen_width = 10.5 - oled_clip_thickness = 1.5 - oled_clip_width = 6.0 - oled_clip_overhang = 1.0 - oled_clip_extension = 5.0 - oled_clip_width_clearance = 0.5 - oled_clip_undercut = 0.5 - oled_clip_undercut_thickness = 2.5 - oled_clip_y_gap = .2 - oled_clip_z_gap = .2 - -web_thickness = 4.0 -post_size = 0.1 -# post_adj = post_size / 2 -post_adj = 0 - -################################### -## Controller Mount / Connectors ## -################################### -# connector options are -# 'RJ9_USB_WALL' = Standard internal plate with RJ9 opening and square cutout for connection. -# 'RJ9_USB_TEENSY' = Teensy holder -# 'EXTERNAL' = square cutout for a holder such as the on from lolligagger. -# controller_mount_type = 'RJ9_USB_WALL' -# controller_mount_type = 'RJ9_USB_TEENSY' -controller_mount_type = 'EXTERNAL' - -external_holder_height = 12.5 -external_holder_width = 28.75 -external_holder_xoffset = -5.0 - - -# Offset is from the top inner corner of the top inner key. - - -################################### -## COLUMN OFFSETS -#################################### - -def column_offset(column: int) -> list: - if column == 2: - return [0, 2.82, -4.5] - elif column >= 4: - return [0, -12, 5.64] # original [0 -5.8 5.64] - else: - return [0, 0, 0] - - -#################################### -## END CONFIGURATION SECTION -#################################### - - - - # Derived values if plate_style in ['NUB', 'HS_NUB']: keyswitch_height = nub_keyswitch_height @@ -263,6 +56,14 @@ if oled_mount_type is not None: left_wall_z_offset = oled_left_wall_z_offset_override +spath = path.join("..", "things", save_dir) +if not path.isdir(spath): + os.mkdir(spath) + +def column_offset(column: int) -> list: + return column_offsets[column] + + def single_plate(cylinder_segments=100, side="right"): if plate_style in ['NUB', 'HS_NUB']: top_wall = sl.cube([mount_width, 1.5, plate_thickness], center=True) @@ -1778,16 +1579,16 @@ def model_side(side="right"): mod_r = model_side(side="right") -sl.scad_render_to_file(mod_r, path.join(r"..", "things", r"right_py.scad")) +sl.scad_render_to_file(mod_r, path.join(r"..", "things", save_dir, config_name + r"_right.scad")) if symmetry == "asymmetric": mod_l = model_side(side="left") sl.scad_render_to_file( - mod_l, path.join(r"..", "things", r"left_py.scad") + mod_l, path.join(r"..", "things", save_dir, config_name + r"_left.scad") ) else: sl.scad_render_to_file( - sl.mirror([-1, 0, 0])(mod_r), path.join(r"..", "things", r"left_py.scad") + sl.mirror([-1, 0, 0])(mod_r), path.join(r"..", "things", save_dir, config_name + r"_left.scad") ) @@ -1801,23 +1602,23 @@ def baseplate(): shape = shape - tool - shape = sl.translate([0, 0, -0.1])(shape) + shape = sl.translate([0, 0, -0.01])(shape) return sl.projection(cut=True)(shape) -sl.scad_render_to_file(baseplate(), path.join(r"..", "things", r"plate_py.scad")) +sl.scad_render_to_file(baseplate(), path.join(r"..", "things", save_dir, config_name + r"_plate.scad")) if oled_mount_type == 'UNDERCUT': - sl.scad_render_to_file(oled_undercut_mount_frame()[1], path.join(r"..", "things", r"oled_undercut_test.scad")) + sl.scad_render_to_file(oled_undercut_mount_frame()[1], path.join(r"..", "things", save_dir, config_name + r"_oled_undercut_test.scad")) if oled_mount_type == 'SLIDING': - sl.scad_render_to_file(oled_sliding_mount_frame()[1], path.join(r"..", "things", r"oled_sliding_test.scad")) + sl.scad_render_to_file(oled_sliding_mount_frame()[1], path.join(r"..", "things", save_dir, config_name + r"_oled_sliding_test.scad")) if oled_mount_type == 'CLIP': oled_mount_location_xyz = (0.0, 0.0, -oled_mount_depth / 2) oled_mount_rotation_xyz = (0.0, 0.0, 0.0) - sl.scad_render_to_file(oled_clip(), path.join(r"..", "things", r"oled_clip.scad")) - sl.scad_render_to_file(oled_clip_mount_frame()[1], path.join(r"..", "things", r"oled_clip_test.scad")) + sl.scad_render_to_file(oled_clip(), path.join(r"..", "things", save_dir, config_name + r"_oled_clip.scad")) + sl.scad_render_to_file(oled_clip_mount_frame()[1], path.join(r"..", "things", save_dir, config_name + r"_oled_clip_test.scad")) sl.scad_render_to_file(oled_clip_mount_frame()[1] + oled_clip(), - path.join(r"..", "things", r"oled_clip_assy_test.scad")) + path.join(r"..", "things", save_dir, config_name + r"_oled_clip_assy_test.scad")) diff --git a/src/dactyl_manuform_cadquery.py b/src/dactyl_manuform_cadquery.py index ddad34f..95a1905 100644 --- a/src/dactyl_manuform_cadquery.py +++ b/src/dactyl_manuform_cadquery.py @@ -3,6 +3,8 @@ import cadquery as cq import numpy as np from numpy import pi import os.path as path +import json +import os from scipy.spatial import ConvexHull as sphull @@ -16,219 +18,24 @@ def rad2deg(rad: float) -> float: debug_exports = False -###################### -## Shape parameters ## -###################### +with open('run_config.json', mode='r') as fid: + data = json.load(fid) +for item in data: + locals()[item] = data[item] -show_caps = False - -nrows = 5 # key rows -ncols = 6 # key columns - -alpha = pi / 12.0 # curvature of the columns -beta = pi / 36.0 # curvature of the rows -centerrow = nrows - 3 # controls front_back tilt -centercol = 3 # controls left_right tilt / tenting (higher number is more tenting) -tenting_angle = pi / 12.0 # or, change this for more precise tenting control - -# symmetry states if it is a symmetric or asymmetric build. If asymmetric it doubles the generation time. -symmetry = "symmetric" # "asymmetric" or "symmetric" +if oled_mount_type is not None: + for item in oled_configurations[oled_mount_type]: + locals()[item] = oled_configurations[oled_mount_type][item] if nrows > 5: - column_style = "orthographic" -else: - column_style = "standard" # options include :standard, :orthographic, and :fixed - -thumb_offsets = [6, -3, 7] -keyboard_z_offset = ( - 9 # controls overall height# original=9 with centercol=3# use 16 for centercol=2 -) + column_style = column_style_gt5 -extra_width = 2.5 # extra space between the base of keys# original= 2 -extra_height = 1.0 # original= 0.5 - -wall_z_offset = 15 # length of the first downward_sloping part of the wall -wall_x_offset = 5 # offset in the x and/or y direction for the first downward_sloping part of the wall (negative) -wall_y_offset = 6 # offset in the x and/or y direction for the first downward_sloping part of the wall (negative) -left_wall_x_offset = 12 # specific values for the left side due to the minimal wall. -left_wall_z_offset = 3 # specific values for the left side due to the minimal wall. -wall_thickness = 4.5 # wall thickness parameter used on upper/mid stage of the wall -wall_base_y_thickness = 4.5 # wall thickness at the lower stage -wall_base_x_thickness = 4.5 # wall thickness at the lower stage - -wall_base_back_thickness = 4.5 # wall thickness at the lower stage in the specifically in back for interface. - -## Settings for column_style == :fixed -## The defaults roughly match Maltron settings -## http://patentimages.storage.googleapis.com/EP0219944A2/imgf0002.png -## fixed_z overrides the z portion of the column ofsets above. -## NOTE: THIS DOESN'T WORK QUITE LIKE I'D HOPED. -fixed_angles = [deg2rad(10), deg2rad(10), 0, 0, 0, deg2rad(-15), deg2rad(-15)] -fixed_x = [-41.5, -22.5, 0, 20.3, 41.4, 65.5, 89.6] # relative to the middle finger -fixed_z = [12.1, 8.3, 0, 5, 10.7, 14.5, 17.5] -fixed_tenting = deg2rad(0) - -####################### -## General variables ## -####################### +centerrow = nrows - centerrow_offset lastrow = nrows - 1 cornerrow = lastrow - 1 lastcol = ncols - 1 -################# -## Switch Hole ## -################# - -# plate options are -# 'HOLE' = a square hole. Also useful for applying custom plate files. -# 'NUB' = original side nubs. -# 'UNDERCUT' = snap fit undercut. May require CLIP_THICKNESS and possibly CLIP_UNDERCUT tweaking -# and/or filing to get proper snap. -# 'HS_NUB' = hot swap underside with nubs. -# 'HS_UNDERCUT' = hot swap underside with undercut. Does not generate properly. Hot swap step needs to be modified. -plate_style = 'UNDERCUT' - -hole_keyswitch_height = 14.0 -hole_keyswitch_width = 14.0 - -nub_keyswitch_height = 14.4 -nub_keyswitch_width = 14.4 - -undercut_keyswitch_height = 14.4 -undercut_keyswitch_width = 14.4 - -sa_profile_key_height = 12.7 -plate_thickness = 4 - -# Undercut style dimensions -clip_thickness = 1.4 -clip_undercut = 1.0 -undercut_transition = .2 # NOT FUNCTIONAL WITH OPENSCAD, ONLY WORKS WITH CADQUERY - -# Custom plate step file -plate_file = None -plate_offset = 0.0 - -########################## -## OLED Mount Location -########################## -# Initial pass will be manual placement. Can be used to create other mounts as well. -# Mount type options: -# None or 'NONE' = No OLED mount -# 'UNDERCUT' = Simple rectangle with undercut for clip in item -# 'SLIDING' = Features to slide the OLED in place and use a pin or block to secure from underneath. -# 'CLIP' = Features to set the OLED in a frame a snap a bezel down to hold it in place. - -oled_mount_type = 'CLIP' - -if oled_mount_type == 'UNDERCUT': - # Common parameters - oled_mount_width = 15.0 - oled_mount_height = 35.0 - oled_mount_rim = 3.0 - oled_mount_depth = 6.0 - oled_mount_cut_depth = 20.0 - oled_mount_location_xyz = (-80.0, 20.0, 45.0) - oled_mount_rotation_xyz = (13.0, 0.0, -6.0) - oled_left_wall_x_offset_override = 28.0 - oled_left_wall_z_offset_override = 0.0 - - # 'UNDERCUT' Parameters - oled_mount_undercut = 1.0 - oled_mount_undercut_thickness = 2.0 - -elif oled_mount_type == 'SLIDING': - # Common parameters - oled_mount_width = 12.5 # width of OLED, plus clearance - oled_mount_height = 25.0 # length of screen - oled_mount_rim = 2.5 - oled_mount_depth = 8.0 - oled_mount_cut_depth = 20.0 - oled_mount_location_xyz = (-78.0, 10.0, 41.0) - oled_mount_rotation_xyz = (6.0, 0.0, -3.0) - oled_left_wall_x_offset_override = 24.0 - oled_left_wall_z_offset_override = 0.0 - - # 'SLIDING' Parameters - oled_thickness = 4.2 # thickness of OLED, plus clearance. Must include components - oled_edge_overlap_end = 6.5 # length from end of viewable screen to end of PCB - oled_edge_overlap_connector = 5.5 # length from end of viewable screen to end of PCB on connection side. - oled_edge_overlap_thickness = 2.5 # thickness of material over edge of PCB - oled_edge_overlap_clearance = 2.5 # Clearance to insert PCB before laying down and sliding. - oled_edge_chamfer = 2.0 - -elif oled_mount_type == 'CLIP': - # Common parameters - oled_mount_width = 12.5 # whole OLED width - oled_mount_height = 39.0 # whole OLED length - oled_mount_rim = 2.0 - oled_mount_depth = 7.0 - oled_mount_cut_depth = 20.0 - oled_mount_location_xyz = (-78.0, 20.0, 42.0) - oled_mount_rotation_xyz = (12.0, 0.0, -6.0) - oled_left_wall_x_offset_override = 24.0 - oled_left_wall_z_offset_override = 0.0 - - # 'CLIP' Parameters - oled_thickness = 4.2 # thickness of OLED, plus clearance. Must include components - oled_mount_bezel_thickness = 3.5 # z thickness of clip bezel - oled_mount_bezel_chamfer = 2.0 # depth of the 45 degree chamfer - oled_mount_connector_hole = 6.0 - oled_screen_start_from_conn_end = 6.5 - oled_screen_length = 24.5 - oled_screen_width = 10.5 - oled_clip_thickness = 1.5 - oled_clip_width = 6.0 - oled_clip_overhang = 1.0 - oled_clip_extension = 5.0 - oled_clip_width_clearance = 0.5 - oled_clip_undercut = 0.5 - oled_clip_undercut_thickness = 2.5 - oled_clip_y_gap = .2 - oled_clip_z_gap = .2 - -web_thickness = 4.0 -post_size = 0.1 -# post_adj = post_size / 2 -post_adj = 0 - -################################### -## Controller Mount / Connectors ## -################################### -# connector options are -# 'RJ9_USB_WALL' = Standard internal plate with RJ9 opening and square cutout for connection. -# 'RJ9_USB_TEENSY' = Teensy holder -# 'EXTERNAL' = square cutout for a holder such as the on from lolligagger. -# controller_mount_type = 'RJ9_USB_WALL' -# controller_mount_type = 'RJ9_USB_TEENSY' -controller_mount_type = 'EXTERNAL' - -external_holder_height = 12.5 -external_holder_width = 28.75 -external_holder_xoffset = -5.0 - - -# Offset is from the top inner corner of the top inner key. - - -################################### -## COLUMN OFFSETS -#################################### - -def column_offset(column: int) -> list: - if column == 2: - return [0, 2.82, -4.5] - elif column >= 4: - return [0, -12, 5.64] # original [0 -5.8 5.64] - else: - return [0, 0, 0] - - -#################################### -## END CONFIGURATION SECTION -#################################### - # Derived values if plate_style in ['NUB', 'HS_NUB']: @@ -255,6 +62,15 @@ if oled_mount_type is not None: left_wall_z_offset = oled_left_wall_z_offset_override + +spath = path.join("..", "things", save_dir) +if not path.isdir(spath): + os.mkdir(spath) + + +def column_offset(column: int) -> list: + return column_offsets[column] + # column_style='fixed' def rotate(shape, angle): @@ -2001,23 +1817,16 @@ def model_side(side="right"): return shape - - -mod_r = model_side(side="right") -cq.exporters.export(w=mod_r, fname=path.join(r"..", "things", r"right_py.step"), exportType='STEP') - -if symmetry == "asymmetric": - mod_l = model_side(side="left") - cq.exporters.export(w=mod_l, fname=path.join(r"..", "things", r"left_py.step"), exportType='STEP') - -else: - cq.exporters.export(w=mod_r.mirror('YZ'), fname=path.join(r"..", "things", r"left_py.step"), exportType='STEP') - - def baseplate(): - shape = mod_r + # shape = mod_r + shape = union([case_walls(), *screw_insert_outers]) + # tool = translate(screw_insert_screw_holes(), [0, 0, -10]) + tool = screw_insert_screw_holes + for item in tool: + item = translate(item, [0, 0, -10]) + shape = shape.cut(item) - shape = shape.translate((0, 0, -0.1)) + shape = shape.translate((0, 0, -0.01)) square = cq.Workplane('XY').rect(1000, 1000) for wire in square.wires().objects: @@ -2025,25 +1834,75 @@ def baseplate(): shape = shape.intersect(plane) + outside = shape.vertices(cq.DirectionMinMaxSelector(cq.Vector(1, 0, 0), True)).objects[0] + + sizes = [] + max_val = 0 + inner_index = 0 + base_wires = shape.wires().objects + for i_wire, wire in enumerate(base_wires): + is_outside = False + for vert in wire.Vertices(): + if vert.toTuple() == outside.toTuple(): + outer_wire = wire + outer_index = i_wire + is_outside = True + sizes.append(0) + if not is_outside: + sizes.append(len(wire.Vertices())) + if sizes[-1]>max_val: + inner_index = i_wire + max_val = sizes[-1] + print(sizes) + inner_wire = base_wires[inner_index] + + inner_plate = cq.Workplane('XY').add(cq.Face.makeFromWires(inner_wire)) + shape.add(inner_plate) + + holes = [] + for i in range(len(base_wires)): + if i not in [inner_index, outer_index]: + holes.append(base_wires[i]) + + shape = cq.Workplane('XY').add(cq.Solid.extrudeLinear(outer_wire, holes, cq.Vector(0, 0, -5))) + return shape + +base = baseplate() +cq.exporters.export(w=base, fname=path.join(r"..", "things", save_dir, config_name + r"_plate.step"), exportType='STEP') +cq.exporters.export(w=base, fname=path.join(r"..", "things", save_dir, config_name + r"_plate.dxf"), exportType='DXF') + +""" +mod_r = model_side(side="right") +cq.exporters.export(w=mod_r, fname=path.join(r"..", "things", save_dir, config_name + r"_right.step"), exportType='STEP') + +if symmetry == "asymmetric": + mod_l = model_side(side="left") + cq.exporters.export(w=mod_l, fname=path.join(r"..", "things", save_dir, config_name + r"_left.step"), exportType='STEP') + +else: + cq.exporters.export(w=mod_r.mirror('YZ'), fname=path.join(r"..", "things", save_dir, config_name + r"_left.step"), exportType='STEP') + + base = baseplate() +cq.exporters.export(w=base, fname=path.join(r"..", "things", save_dir, config_name + r"_plate.step"), exportType='STEP') +cq.exporters.export(w=base, fname=path.join(r"..", "things", save_dir, config_name + r"_plate.dxf"), exportType='DXF') -cq.exporters.export(w=base, fname=path.join(r"..", "things", r"plate_py.step"), exportType='STEP') -cq.exporters.export(w=base, fname=path.join(r"..", "things", r"plate_py.dxf"), exportType='DXF') if oled_mount_type == 'UNDERCUT': - cq.exporters.export(w=oled_undercut_mount_frame()[1], fname=path.join(r"..", "things", r"oled_undercut_test.step"), exportType='STEP') + cq.exporters.export(w=oled_undercut_mount_frame()[1], fname=path.join(r"..", "things", save_dir, config_name + r"_oled_undercut_test.step"), exportType='STEP') if oled_mount_type == 'SLIDING': - cq.exporters.export(w=oled_sliding_mount_frame()[1], fname=path.join(r"..", "things", r"oled_sliding_test.step"), exportType='STEP') + cq.exporters.export(w=oled_sliding_mount_frame()[1], fname=path.join(r"..", "things", save_dir, config_name + r"_oled_sliding_test.step"), exportType='STEP') if oled_mount_type == 'CLIP': oled_mount_location_xyz = (0.0, 0.0, -oled_mount_depth / 2) oled_mount_rotation_xyz = (0.0, 0.0, 0.0) - cq.exporters.export(w=oled_clip(), fname=path.join(r"..", "things", r"oled_clip.step"), exportType='STEP') + cq.exporters.export(w=oled_clip(), fname=path.join(r"..", "things", save_dir, config_name + r"_oled_clip.step"), exportType='STEP') cq.exporters.export(w=oled_clip_mount_frame()[1], - fname=path.join(r"..", "things", r"oled_clip_test.step"), exportType='STEP') + fname=path.join(r"..", "things", save_dir, config_name + r"_oled_clip_test.step"), exportType='STEP') cq.exporters.export(w=union((oled_clip_mount_frame()[1], oled_clip())), - fname=path.join(r"..", "things", r"oled_clip_assy_test.step"), exportType='STEP') + fname=path.join(r"..", "things", save_dir, config_name + r"_oled_clip_assy_test.step"), exportType='STEP') +""" \ No newline at end of file diff --git a/src/generate_configuration.py b/src/generate_configuration.py new file mode 100644 index 0000000..98ef12c --- /dev/null +++ b/src/generate_configuration.py @@ -0,0 +1,229 @@ +import json + + +pi = 3.14159 +d2r = pi / 180 +r2d = 180 / pi + +shape_config = { + + # 'ENGINE': 'solid', # 'solid' = solid python / OpenSCAD, 'cadquery' = cadquery / OpenCascade + # 'ENGINE': 'cadquery', # 'solid' = solid python / OpenSCAD, 'cadquery' = cadquery / OpenCascade + + + ###################### + ## Shape parameters ## + ###################### + + 'save_dir': '.', + 'config_name': "DM", + + 'show_caps': False, + + 'nrows': 5, # key rows + 'ncols': 6, # key columns + + 'alpha': pi / 12.0, # curvature of the columns + 'beta': pi / 36.0, # curvature of the rows + 'centerrow_offset': 3, # rows from max, controls front_back tilt + 'centercol': 3, # controls left_right tilt / tenting (higher number is more tenting) + 'tenting_angle': pi / 12.0, # or, change this for more precise tenting control + + # symmetry states if it is a symmetric or asymmetric build. If asymmetric it doubles the generation time. + 'symmetry': "symmetric", # "asymmetric" or "symmetric" + + 'column_style_gt5': "orthographic", + 'column_style': "standard", # options include :standard, :orthographic, and :fixed + + 'thumb_offsets': [6, -3, 7], + 'keyboard_z_offset': ( + 9 # controls overall height# original=9 with centercol=3# use 16 for centercol=2 + ), + + 'extra_width': 2.5, # extra space between the base of keys# original= 2 + 'extra_height': 1.0, # original= 0.5 + + 'wall_z_offset': 15, # length of the first downward_sloping part of the wall + 'wall_x_offset': 5, # offset in the x and/or y direction for the first downward_sloping part of the wall (negative) + 'wall_y_offset': 6, # offset in the x and/or y direction for the first downward_sloping part of the wall (negative) + 'left_wall_x_offset': 12, # specific values for the left side due to the minimal wall. + 'left_wall_z_offset': 3, # specific values for the left side due to the minimal wall. + 'wall_thickness': 4.5, # wall thickness parameter used on upper/mid stage of the wall + 'wall_base_y_thickness': 4.5, # wall thickness at the lower stage + 'wall_base_x_thickness': 4.5, # wall thickness at the lower stage + + 'wall_base_back_thickness': 4.5, # wall thickness at the lower stage in the specifically in back for interface. + + ## Settings for column_style == :fixed + ## The defaults roughly match Maltron settings + ## http://patentimages.storage.googleapis.com/EP0219944A2/imgf0002.png + ## fixed_z overrides the z portion of the column ofsets above. + ## NOTE: THIS DOESN'T WORK QUITE LIKE I'D HOPED. + 'fixed_angles': [d2r * 10, d2r * 10, 0, 0, 0, d2r * -15, d2r * -15], + 'fixed_x': [-41.5, -22.5, 0, 20.3, 41.4, 65.5, 89.6], # relative to the middle finger + 'fixed_z': [12.1, 8.3, 0, 5, 10.7, 14.5, 17.5], + 'fixed_tenting': d2r * 0, + + ################# + ## Switch Hole ## + ################# + + # plate options are + # 'HOLE' = a square hole. Also useful for applying custom plate files. + # 'NUB' = original side nubs. + # 'UNDERCUT' = snap fit undercut. May require CLIP_THICKNESS and possibly CLIP_UNDERCUT tweaking + # and/or filing to get proper snap. + # 'HS_NUB' = hot swap underside with nubs. + # 'HS_UNDERCUT' = hot swap underside with undercut. Does not generate properly. Hot swap step needs to be modified. + 'plate_style': 'UNDERCUT', + + 'hole_keyswitch_height': 14.0, + 'hole_keyswitch_width': 14.0, + + 'nub_keyswitch_height': 14.4, + 'nub_keyswitch_width': 14.4, + + 'undercut_keyswitch_height': 14.4, + 'undercut_keyswitch_width': 14.4, + + 'sa_profile_key_height': 12.7, + 'plate_thickness': 4, + + # Undercut style dimensions + 'clip_thickness': 1.4, + 'clip_undercut': 1.0, + 'undercut_transition': .2, # NOT FUNCTIONAL WITH OPENSCAD, ONLY WORKS WITH CADQUERY + + # Custom plate step file + 'plate_file': None, + 'plate_offset': 0.0, + + ########################## + ## OLED Mount Location + ########################## + # Initial pass will be manual placement. Can be used to create other mounts as well. + # Mount type options: + # None or 'NONE' = No OLED mount + # 'UNDERCUT' = Simple rectangle with undercut for clip in item + # 'SLIDING' = Features to slide the OLED in place and use a pin or block to secure from underneath. + # 'CLIP' = Features to set the OLED in a frame a snap a bezel down to hold it in place. + + 'oled_mount_type': 'CLIP', + + 'oled_configurations': { + 'UNDERCUT':{ + # Common parameters + 'oled_mount_width': 15.0, + 'oled_mount_height': 35.0, + 'oled_mount_rim': 3.0, + 'oled_mount_depth': 6.0, + 'oled_mount_cut_depth': 20.0, + 'oled_mount_location_xyz': (-80.0, 20.0, 45.0), + 'oled_mount_rotation_xyz': (13.0, 0.0, -6.0), + 'oled_left_wall_x_offset_override': 28.0, + 'oled_left_wall_z_offset_override': 0.0, + + # 'UNDERCUT' Parameters + 'oled_mount_undercut': 1.0, + 'oled_mount_undercut_thickness': 2.0, + }, + 'SLIDING': { + # Common parameters + 'oled_mount_width': 12.5, # width of OLED, plus clearance + 'oled_mount_height': 25.0, # length of screen + 'oled_mount_rim': 2.5, + 'oled_mount_depth': 8.0, + 'oled_mount_cut_depth': 20.0, + 'oled_mount_location_xyz': (-78.0, 10.0, 41.0), + 'oled_mount_rotation_xyz': (6.0, 0.0, -3.0), + 'oled_left_wall_x_offset_override': 24.0, + 'oled_left_wall_z_offset_override': 0.0, + + # 'SLIDING' Parameters + 'oled_thickness': 4.2, # thickness of OLED, plus clearance. Must include components + 'oled_edge_overlap_end': 6.5, # length from end of viewable screen to end of PCB + 'oled_edge_overlap_connector': 5.5, # length from end of viewable screen to end of PCB on connection side. + 'oled_edge_overlap_thickness': 2.5, # thickness of material over edge of PCB + 'oled_edge_overlap_clearance': 2.5, # Clearance to insert PCB before laying down and sliding. + 'oled_edge_chamfer': 2.0, + }, + 'CLIP': { + # Common parameters + 'oled_mount_width': 12.5, # whole OLED width + 'oled_mount_height': 39.0, # whole OLED length + 'oled_mount_rim': 2.0, + 'oled_mount_depth': 7.0, + 'oled_mount_cut_depth': 20.0, + 'oled_mount_location_xyz': (-78.0, 20.0, 42.0), + 'oled_mount_rotation_xyz': (12.0, 0.0, -6.0), + 'oled_left_wall_x_offset_override': 24.0, + 'oled_left_wall_z_offset_override': 0.0, + + # 'CLIP' Parameters + 'oled_thickness': 4.2, # thickness of OLED, plus clearance. Must include components + 'oled_mount_bezel_thickness': 3.5, # z thickness of clip bezel + 'oled_mount_bezel_chamfer': 2.0, # depth of the 45 degree chamfer + 'oled_mount_connector_hole': 6.0, + 'oled_screen_start_from_conn_end': 6.5, + 'oled_screen_length': 24.5, + 'oled_screen_width': 10.5, + 'oled_clip_thickness': 1.5, + 'oled_clip_width': 6.0, + 'oled_clip_overhang': 1.0, + 'oled_clip_extension': 5.0, + 'oled_clip_width_clearance': 0.5, + 'oled_clip_undercut': 0.5, + 'oled_clip_undercut_thickness': 2.5, + 'oled_clip_y_gap': .2, + 'oled_clip_z_gap': .2, + } + }, + 'web_thickness': 4.0, + 'post_size': 0.1, + # post_adj': post_size / 2 + 'post_adj': 0, + + ################################### + ## Controller Mount / Connectors ## + ################################### + # connector options are + # 'RJ9_USB_WALL' = Standard internal plate with RJ9 opening and square cutout for connection. + # 'RJ9_USB_TEENSY' = Teensy holder + # 'EXTERNAL' = square cutout for a holder such as the on from lolligagger. + # controller_mount_type = 'RJ9_USB_WALL' + # controller_mount_type = 'RJ9_USB_TEENSY' + 'controller_mount_type': 'EXTERNAL', + + 'external_holder_height': 12.5, + 'external_holder_width': 28.75, + 'external_holder_xoffset': -5.0, + + + # Offset is from the top inner corner of the top inner key. + + + ################################### + ## COLUMN OFFSETS + #################################### + + 'column_offsets': [ + [0, 0, 0], + [0, 0, 0], + [0, 2.82, -4.5], + [0, 0, 0], + [0, -12, 5.64], + [0, -12, 5.64], + [0, -12, 5.64], + ], + +} + + #################################### + ## END CONFIGURATION SECTION + #################################### + + + +with open('run_config.json', mode='w') as fid: + json.dump(shape_config, fid, indent=4) + diff --git a/src/model_builder.py b/src/model_builder.py new file mode 100644 index 0000000..0a6a6a8 --- /dev/null +++ b/src/model_builder.py @@ -0,0 +1,134 @@ +import os +import copy +import importlib +from src.generate_configuration import * + +ENGINE = 'solid' +# ENGINE = 'cadquery' + +base = shape_config + +configurations = [ + { + 'config_name': '4x5_OLED_CtrlTray', + 'save_dir': '4x5_OLED_CtrlTray', + 'nrows': 4, # key rows + 'ncols': 5, # key columns + 'oled_mount_type': 'CLIP', + 'controller_mount_type': 'EXTERNAL', + }, + { + 'config_name': '4x6_OLED_CtrlTray', + 'save_dir': '4x6_OLED_CtrlTray', + 'nrows': 4, # key rows + 'ncols': 6, # key columns + 'oled_mount_type': 'CLIP', + 'controller_mount_type': 'EXTERNAL', + }, + { + 'config_name': '5x6_OLED_CtrlTray', + 'save_dir': '5x6_OLED_CtrlTray', + 'nrows': 5, # key rows + 'ncols': 6, # key columns + 'oled_mount_type': 'CLIP', + 'controller_mount_type': 'EXTERNAL', + }, + { + 'config_name': '6x6_OLED_CtrlTray', + 'save_dir': '6x6_OLED_CtrlTray', + 'nrows': 6, # key rows + 'ncols': 6, # key columns + 'oled_mount_type': 'CLIP', + 'controller_mount_type': 'EXTERNAL', + }, + { + 'config_name': '4x5_CtrlTray', + 'save_dir': '4x5_CtrlTray', + 'nrows': 4, # key rows + 'ncols': 5, # key columns + 'oled_mount_type': None, + 'controller_mount_type': 'EXTERNAL', + }, + { + 'config_name': '4x6_CtrlTray', + 'save_dir': '4x6_CtrlTray', + 'nrows': 4, # key rows + 'ncols': 6, # key columns + 'oled_mount_type': None, + 'controller_mount_type': 'EXTERNAL', + }, + { + 'config_name': '5x6_CtrlTray', + 'save_dir': '5x6_CtrlTray', + 'nrows': 5, # key rows + 'ncols': 6, # key columns + 'oled_mount_type': None, + 'controller_mount_type': 'EXTERNAL', + }, + { + 'config_name': '6x6_CtrlTray', + 'save_dir': '6x6_CtrlTray', + 'nrows': 6, # key rows + 'ncols': 6, # key columns + 'oled_mount_type': 'CLIP', + 'controller_mount_type': 'EXTERNAL', + }, + { + 'config_name': '4x5_Basic', + 'save_dir': '4x5_Basic', + 'nrows': 4, # key rows + 'ncols': 5, # key columns + 'oled_mount_type': None, + 'controller_mount_type': 'EXTERNAL', + }, + { + 'config_name': '4x6_Basic', + 'save_dir': '4x6_Basic', + 'nrows': 4, # key rows + 'ncols': 6, # key columns + 'oled_mount_type': None, + 'controller_mount_type': 'RJ9_USB_WALL', + }, + { + 'config_name': '5x6_Basic', + 'save_dir': '5x6_Basic', + 'nrows': 5, # key rows + 'ncols': 6, # key columns + 'oled_mount_type': None, + 'controller_mount_type': 'RJ9_USB_WALL', + }, + { + 'config_name': '6x6_Basic', + 'save_dir': '6x6_Basic', + 'nrows': 6, # key rows + 'ncols': 6, # key columns + 'oled_mount_type': None, + 'controller_mount_type': 'RJ9_USB_WALL', + } +] + +init = True + +for config in configurations: + shape_config = copy.deepcopy(base) + for item in config: + shape_config[item] = config[item] + + + with open('run_config.json', mode='w') as fid: + json.dump(shape_config, fid, indent=4) + + for ENGINE in ['solid', 'cadquery']: + if ENGINE == 'solid': + if init: + import src.dactyl_manuform as dactyl_manuform + else: + importlib.reload(dactyl_manuform) + + if ENGINE == 'cadquery': + if init: + import src.dactyl_manuform_cadquery as dactyl_manuform_cadquery + else: + importlib.reload(dactyl_manuform_cadquery) + + init = False diff --git a/src/run.py b/src/run.py new file mode 100644 index 0000000..2c61511 --- /dev/null +++ b/src/run.py @@ -0,0 +1,15 @@ +# System will run the current "run_config.json" file in the directory. +# Use generator configuration to create it, or edit it directly, depending on your +# preference. You may want to back it up as another file name to prevent the generator from +# overwriting your config history. + + + +ENGINE = 'solid' +# ENGINE = 'cadquery' + +if ENGINE == 'solid': + exec(open('dactyl_manuform.py').read()) + +if ENGINE == 'cadquery': + exec(open('dactyl_manuform_cadquery.py').read()) diff --git a/src/run_config.json b/src/run_config.json new file mode 100644 index 0000000..ae63aab --- /dev/null +++ b/src/run_config.json @@ -0,0 +1,202 @@ +{ + "save_dir": "6x6_Basic", + "config_name": "6x6_Basic", + "show_caps": false, + "nrows": 6, + "ncols": 6, + "alpha": 0.26179916666666664, + "beta": 0.08726638888888888, + "centerrow_offset": 3, + "centercol": 3, + "tenting_angle": 0.26179916666666664, + "symmetry": "symmetric", + "column_style_gt5": "orthographic", + "column_style": "standard", + "thumb_offsets": [ + 6, + -3, + 7 + ], + "keyboard_z_offset": 9, + "extra_width": 2.5, + "extra_height": 1.0, + "wall_z_offset": 15, + "wall_x_offset": 5, + "wall_y_offset": 6, + "left_wall_x_offset": 12, + "left_wall_z_offset": 3, + "wall_thickness": 4.5, + "wall_base_y_thickness": 4.5, + "wall_base_x_thickness": 4.5, + "wall_base_back_thickness": 4.5, + "fixed_angles": [ + 0.17453277777777776, + 0.17453277777777776, + 0, + 0, + 0, + -0.26179916666666664, + -0.26179916666666664 + ], + "fixed_x": [ + -41.5, + -22.5, + 0, + 20.3, + 41.4, + 65.5, + 89.6 + ], + "fixed_z": [ + 12.1, + 8.3, + 0, + 5, + 10.7, + 14.5, + 17.5 + ], + "fixed_tenting": 0.0, + "plate_style": "UNDERCUT", + "hole_keyswitch_height": 14.0, + "hole_keyswitch_width": 14.0, + "nub_keyswitch_height": 14.4, + "nub_keyswitch_width": 14.4, + "undercut_keyswitch_height": 14.4, + "undercut_keyswitch_width": 14.4, + "sa_profile_key_height": 12.7, + "plate_thickness": 4, + "clip_thickness": 1.4, + "clip_undercut": 1.0, + "undercut_transition": 0.2, + "plate_file": null, + "plate_offset": 0.0, + "oled_mount_type": null, + "oled_configurations": { + "UNDERCUT": { + "oled_mount_width": 15.0, + "oled_mount_height": 35.0, + "oled_mount_rim": 3.0, + "oled_mount_depth": 6.0, + "oled_mount_cut_depth": 20.0, + "oled_mount_location_xyz": [ + -80.0, + 20.0, + 45.0 + ], + "oled_mount_rotation_xyz": [ + 13.0, + 0.0, + -6.0 + ], + "oled_left_wall_x_offset_override": 28.0, + "oled_left_wall_z_offset_override": 0.0, + "oled_mount_undercut": 1.0, + "oled_mount_undercut_thickness": 2.0 + }, + "SLIDING": { + "oled_mount_width": 12.5, + "oled_mount_height": 25.0, + "oled_mount_rim": 2.5, + "oled_mount_depth": 8.0, + "oled_mount_cut_depth": 20.0, + "oled_mount_location_xyz": [ + -78.0, + 10.0, + 41.0 + ], + "oled_mount_rotation_xyz": [ + 6.0, + 0.0, + -3.0 + ], + "oled_left_wall_x_offset_override": 24.0, + "oled_left_wall_z_offset_override": 0.0, + "oled_thickness": 4.2, + "oled_edge_overlap_end": 6.5, + "oled_edge_overlap_connector": 5.5, + "oled_edge_overlap_thickness": 2.5, + "oled_edge_overlap_clearance": 2.5, + "oled_edge_chamfer": 2.0 + }, + "CLIP": { + "oled_mount_width": 12.5, + "oled_mount_height": 39.0, + "oled_mount_rim": 2.0, + "oled_mount_depth": 7.0, + "oled_mount_cut_depth": 20.0, + "oled_mount_location_xyz": [ + -78.0, + 20.0, + 42.0 + ], + "oled_mount_rotation_xyz": [ + 12.0, + 0.0, + -6.0 + ], + "oled_left_wall_x_offset_override": 24.0, + "oled_left_wall_z_offset_override": 0.0, + "oled_thickness": 4.2, + "oled_mount_bezel_thickness": 3.5, + "oled_mount_bezel_chamfer": 2.0, + "oled_mount_connector_hole": 6.0, + "oled_screen_start_from_conn_end": 6.5, + "oled_screen_length": 24.5, + "oled_screen_width": 10.5, + "oled_clip_thickness": 1.5, + "oled_clip_width": 6.0, + "oled_clip_overhang": 1.0, + "oled_clip_extension": 5.0, + "oled_clip_width_clearance": 0.5, + "oled_clip_undercut": 0.5, + "oled_clip_undercut_thickness": 2.5, + "oled_clip_y_gap": 0.2, + "oled_clip_z_gap": 0.2 + } + }, + "web_thickness": 4.0, + "post_size": 0.1, + "post_adj": 0, + "controller_mount_type": "RJ9_USB_WALL", + "external_holder_height": 12.5, + "external_holder_width": 28.75, + "external_holder_xoffset": -5.0, + "column_offsets": [ + [ + 0, + 0, + 0 + ], + [ + 0, + 0, + 0 + ], + [ + 0, + 2.82, + -4.5 + ], + [ + 0, + 0, + 0 + ], + [ + 0, + -12, + 5.64 + ], + [ + 0, + -12, + 5.64 + ], + [ + 0, + -12, + 5.64 + ] + ] +} \ No newline at end of file -- cgit v1.2.3