Files
braille_translator/model_gen/translate_braille_2_scad.py
2024-04-08 14:09:47 +01:00

665 lines
30 KiB
Python

# -*- coding: utf-8 -*-
"""
Created on Wed Apr 26 17:11:57 2023
@author: Edward Middleton-Smith
Braille 3D Model Product Creation
Plaintext message translation into Braille for 3D modelling
Procedural OpenSCAD Generation
Braille Scrabble Pieces
"""
# CLASSES
# ATTRIBUTE DECLARATION
# METHODS
# FUNCTION
# ARGUMENTS
# ARGUMENT VALIDATION
# ATTRIBUTE + VARIABLE INSTANTIATION
# METHODS
# RETURNS
# NORMAL METHODS
# FUNCTION
# ARGUMENTS
# ARGUMENT VALIDATION
# VARIABLE INSTANTIATION
# METHODS
# RETURNS
import openpyscad as ops
import numpy as np
import argument_validation as av
from openscad_objects import gen_openscad_colours
from typing import Optional
from prettytable import PrettyTable
from enum import Enum
class elem_visibility(Enum):
EMBOSSED = -1
HIDDEN = 0
VISIBLE = 1
def mini():
# FUNCTION
# Get minimum value in enumerator
# RETURNS
return min([e.value for e in elem_visibility])
def maxi():
# FUNCTION
# Get maximum value in enumerator
# RETURNS
return max([e.value for e in elem_visibility])
class style():
# ATTRIBUTE DECLARATION
braille: elem_visibility
letter: elem_visibility
number: elem_visibility
def __new__(cls, braille, letter, number):
_m = 'style.__new__'
av.val_type(braille, "<enum 'elem_visibility'>", 'braille', _m)
av.val_type(braille, "<enum 'elem_visibility'>", 'braille', _m)
av.val_type(braille, "<enum 'elem_visibility'>", 'braille', _m)
return super(style, cls).__new__(cls)
def __init__(self, braille, letter, number):
self.braille = braille
self.letter = letter
self.number = number
def gen_filetxt(mystyle):
# FUNCTION
# generate filename text for style settings
# ARGUMENTS
# style mystyle
# ARGUMENT VALIDATION
av.val_type(mystyle, "<class 'translate_braille_2_scad.style'>", 'mystyle', 'get_style_filetxt')
# VARIABLE INSTANTIATION
style_txt = '_0b' if (mystyle.braille == elem_visibility.HIDDEN) else ''
style_txt += '_0l' if (mystyle.letter == elem_visibility.HIDDEN) else ''
style_txt += '__l' if (mystyle.letter == elem_visibility.EMBOSSED) else ''
style_txt += '_0n' if (mystyle.number == elem_visibility.HIDDEN) else ''
# RETURNS
return style_txt
def base_fillet_cube(a, b, c, f, q, centre = "true"):
# fillets removed as they print horribly
# now just cube
my_dif = ops.Difference();
my_dif.append(ops.Cube([a, b, c], center = "true", _fn = q));
return my_dif.translate([0 if (centre == "true") else a/2, 0 if (centre == "true") else b/2, 0 if (centre == "true") else c/2])
def triprism(a, L, centre = "true"):
return ops.Polygon([[0,0], [0, a], [a, 0]]).linear_extrude(L, center = "true").translate([0, 0, 0 if (centre == "true") else L/2]);
class scrabble_dimensions:
# ATTRIBUTE DECLARATION
name: str
dp: float # distance between dots
hblock: float # height of
s: float # spacing between keys on keyboard
base_height: float # height of
hcyl: float # height of Braille dots
rcyl: float # base radius of Braille dots
rsphere: float # top radius of Braille dots
# METHODS
def __new__(cls, name, dp, hblock, s, base_height, hcyl, rcyl, rsphere):
# FUNCTION
# Initialise class object
# ARGUMENTS
# str name - name size scheme
# float dp - distance between braille pins
# float hblock - height of base of each key
# float s - spacing
# float base_height - height of base block - board
# float hcyl - height of braille pins
# float rcyl - base radius of braille pins
# float rsphere - tip radius of braille pins
# ARGUMENT VALIDATION
_m = 'scrabble_dimensions.__new__'
v_arg_type = 'class attribute'
av.val_str(name, 'name', _m, 1, v_arg_type = v_arg_type)
av.full_val_float(dp, 'dp', _m, 0, v_arg_type = v_arg_type)
av.full_val_float(hblock, 'hblock', _m, v_arg_type = v_arg_type)
av.full_val_float(s, 's', _m, 0, v_arg_type = v_arg_type)
av.full_val_float(base_height, 'base_height', _m, 0, v_arg_type = v_arg_type)
av.full_val_float(hcyl, 'hcyl', _m, 0, v_arg_type = v_arg_type)
av.full_val_float(rcyl, 'rcyl', _m, 0, v_arg_type = v_arg_type)
av.full_val_float(rsphere, 'rsphere', _m, 0, v_arg_type = v_arg_type)
# RETURNS
return super(scrabble_dimensions, cls).__new__(cls)
def __init__(self, name, dp, hblock, s, base_height, hcyl, rcyl, rsphere):
# FUNCTION
# Construct class object
# ARGUMENTS
# str name
# float dp
# float hblock
# float s
# float base_height
# float hcyl
# float rcyl
# float rsphere
# ARGUMENT VALIDATION
# see __new__()
# ATTRIBUTE INSTANTIATION
self.name = name
self.dp = dp
self.hblock = hblock
self.s = s
self.base_height = base_height
self.hcyl = hcyl
self.rcyl = rcyl
self.rsphere = rsphere
def __repr__(self):
# FUNCTION
# Convert object to str representation
# RETURNS
return f"name = {self.name}, dp = {self.dp}, hblock = {self.hblock}, s = {self.s}, base_height = {self.base_height}, hcyl = {self.hcyl}, rcyl = {self.rcyl}, rsphere = {self.rsphere}"
def as_dict(self):
# FUNCTION
# Convert object attribute, value pairs to dictionary representation
# RETURNS
return {'name': self.name, 'dp': self.dp, 'hblock': self.hblock, 's': self.s, 'base_height': self.base_height, 'hcyl': self.hcyl, 'rcyl': self.rcyl, 'rsphere': self.rsphere}
# colour themes: # ToDo: include in arguments to generator!!
class export_colour_theme:
# ATTRIBUTE DECLARATION
name: str
coltop: str
colbase: str
# METHODS
def __new__(cls, name, coltop, colbase, openscad_colours):
# FUNCTION
# Initialise class object
# ARGUMENTS
# str name - user reference to colour theme
# str coltop - a valid openSCAD colour e.g. '#010101'
# str colbase - a valid openSCAD colour e.g. 'blue'
# list[str] openscad_colours
# ARGUMENT VALIDATION
_m = 'export_colour_theme.__new__'
v_arg_type = 'class attribute'
av.val_str(name, 'name', _m, 1, v_arg_type = v_arg_type)
av.val_list(openscad_colours, 'openscad_colours', _m, "<class 'str'>", 1, v_arg_type = v_arg_type)
if not validate_openscad_colour(coltop, openscad_colours):
raise ValueError(f"Invalid export_colour_theme attribute coltop. Type = {str(type(coltop))}. Value = {coltop}")
if not validate_openscad_colour(colbase, openscad_colours):
raise ValueError(f"Invalid export_colour_theme attribute colbase. Type = {str(type(colbase))}. Value = {colbase}")
# RETURNS
return super(export_colour_theme, cls).__new__(cls)
def __init__(self, name, coltop, colbase, openscad_colours):
# FUNCTION
# Initialise class object
# ARGUMENTS
# str name - user reference to colour theme
# str coltop - a valid openSCAD colour e.g. '#010101'
# str colbase - a valid openSCAD colour e.g. 'blue'
# list[str] openscad_colours # redundant in this function - used for argument validation
# ARGUMENT VALIDATION
# see __new__()
# ATTRIBUTE INSTANTIATION
self.name = name
self.top = coltop
self.base = colbase
def __repr__(self):
# FUNCTION
# Convert object to str representation
# RETURNS
return f"name = {self.name}, colour top = {self.top}, colour base = {self.base}" # , openscad_colours = {self.openscad_colours}
def as_dict(self):
# FUNCTION
# Convert object attribute, value pairs to dictionary representation
# RETURNS
return {'name': self.name, 'colour top': self.top, 'colour base': self.base} # , 'openscad_colours': self.openscad_colours
def as_row(self):
# FUNCTION
# Convert object values to list representation
# RETURNS
return [self.name, self.top, self.base] # , 'openscad_colours': self.openscad_colours
def gen_scrabble_sizes(s: Optional[float] = 0.5):
# default scrabble sizes
# s = 1/2
# def __init__(self, name, dp, hblock, s, base_height, hcyl, rcyl, rsphere):
k = 2.3622
szs_scrabble = []
szs_scrabble.append(scrabble_dimensions('sz_1', 2.5, 2*s, s, 4*s, 1.2, 2/3, 1/2))
szs_scrabble.append(scrabble_dimensions('sz_2', 4, 1.28, s, 4*s, 1.2, 3/2, 1))
szs_scrabble.append(scrabble_dimensions('sz_3', 8, 2.5, s, 6*s, 1.2, 3/2, 1))
szs_scrabble.append(scrabble_dimensions('sz_4', 10, 3, 1, 6*s, 1.2, 3/2, 1))
szs_scrabble.append(scrabble_dimensions('sz_5', 10, 1, 1, 6*s, 1.2, 3/2, 1))
szs_scrabble.append(scrabble_dimensions('keyboard', 2.5, 0.8, 0.5, 4, 1.2, 2/3, 0.5))
szs_scrabble.append(scrabble_dimensions('poster', 2.5, 0.8, 0.5, 2, 1.2, 2/3, 0.5))
szs_scrabble.append(scrabble_dimensions('poster_big', 2.5*k, 0.8, 0.5*k, 2, 1.2, 2/3*k, 0.5*k))
# print('szs_scrabble')
# print(szs_scrabble)
return szs_scrabble # pd.DataFrame([x.as_dict() for x in szs_scrabble])
def input_product_size(szs_product, v_size = 2):
# FUNCTION
# input valid product size from user
# ARGUMENTS
# list[scrabble_dimensions] szs_product
# int v_size
# ARGUMENT VALIDATION
_m = 'get_product_size'
av.val_list(szs_product, 'szs_product', _m, "<class 'translate_braille_2_scad.scrabble_dimensions'>")
av.val_int(v_size, 'v_size', _m)
# VARIABLE INSTANTIATION
n = len(szs_product)
# METHODS
if v_size > 0 and v_size <= n:
return szs_product[v_size - 1]
my_table = PrettyTable()
my_table.field_names = ['index', 'name', 'dp', 'hblock', 's', 'base_height', 'hcyl', 'rcyl', 'rsphere']
for i in range(n):
prod = szs_product[i]
my_table.add_row([i + 1, prod.name, prod.dp, prod.hblock, prod.s, prod.base_height, prod.hcyl, prod.rcyl, prod.rsphere])
print()
print("Please select product dimensions configuration from below:")
print(my_table)
# loopy = False
while True:
my_input = input("Product dimensions configuration (name or index): ")
if my_input == "#!ERRORCODE!#": exit
for i in range(n):
if validate_input_product_size(my_input, szs_product):
# loopy = True
# RETURNS
return get_product_size(my_input, szs_product)
def get_product_size(product_option, szs_product):
# FUNCTION
# get valid product size from list of scrabble_dimensions
# ARGUMENTS
# str/int product_option
# list[scrabble_dimensions] szs_product
# ARGUMENT VALIDATION
_m = 'get_product_size'
error_msg = av.error_msg_str(_m, product_option, 'product_option', 'scrabble_dimensions identifier')
if not (av.val_int(product_option, 'product_option', _m, suppress_errors = True, suppress_console_outputs = True) or av.val_str(product_option, 'product_option', _m, suppress_errors = True, suppress_console_outputs = True)):
raise ValueError(error_msg)
av.val_list(szs_product, 'szs_product', _m, "<class 'translate_braille_2_scad.scrabble_dimensions'>")
# VARIABLE INSTANTIATION
n = len(szs_product)
# METHODS
if av.full_val_int(product_option, 'product_option', _m, suppress_errors = True):
product_option = av.input_int(product_option, 'product_option', _m)
return szs_product[product_option - 1]
for col_i in range(n):
my_product = szs_product[col_i]
if product_option == my_product.name:
return my_product
# RETURNS
raise ValueError(error_msg)
def validate_input_product_size(product_option, szs_product):
# FUNCTION
# evaluate if product_option relates to a szs_product
# ARGUMENTS
# str/int product_option
# list[scrabble_dimensions] szs_product
# ARGUMENT VALIDATION
_m = 'validate_input_product_size'
if not (av.val_int(product_option, 'product_option', _m, suppress_errors = True, suppress_console_outputs = True) or av.val_str(product_option, 'product_option', _m, suppress_errors = True, suppress_console_outputs = True)): return False
av.val_list(szs_product, 'szs_product', _m, "<class 'translate_braille_2_scad.scrabble_dimensions'>")
# VARIABLE INSTANTIATION
n = len(szs_product)
# METHODS
if av.full_val_int(product_option, 'product_option', _m, suppress_errors = True):
product_option = av.input_int(product_option, 'product_option', _m)
return (0 < product_option and product_option <= n)
for prod_i in range(n):
if product_option == szs_product[prod_i].name:
return True
# RETURNS
return False
def validate_openscad_colour(mycolour, openscad_colours):
# FUNCTION
# validate argument openscad colour as string
# ARGUMENTS
# str mycolour - to be validated
# list[str] openscad_colours - to search through
# ARGUMENT VALIDATION
_m = 'validate_openscad_colour'
av.val_str(mycolour, 'mycolour', _m)
av.val_list(openscad_colours, 'openscad_colours', _m, "<class 'str'>")
# VARIABLE INSTANTIATION
N = len(mycolour)
# METHODS
if (N == 7):
if (mycolour[0] == '#' and mycolour[0:5].isnumeric):
return True
for i in range(len(openscad_colours)):
if (mycolour == openscad_colours[i]):
return True
# RETURNS
return False
# inputs
# path_scad = "C:/Users/edwar/OneDrive/Documents/OpenSCAD"
# path_stl = "C:/Users/edwar/OneDrive/Documents/OpenSCAD/STL"
# filename = "Braille_Scrabble_"
# cum = [0, 1]
# letters = ["B"]
# braille = [[2]]
# braille = [[[1, 1, 0, 0, 0, 0]]]
# lmsg = 1
# N = 1
# n_row_keys = 1
# maxln = 2
def gen_col_themes():
# FUNCTION
# create colour themes for image export of 3D models
# REQUIRES
# class export_colour_theme
# function gen_openscad_colours
# VARIABLE INSTANTIATION
openscad_colours = gen_openscad_colours()
col_themes = []
col_themes.append(export_colour_theme("Blue", "#337AFF", "#337AFF", openscad_colours))
col_themes.append(export_colour_theme("Purple", "#8E44AD", "#8E44AD", openscad_colours))
col_themes.append(export_colour_theme("Red", "#F7322F", "#F7322F", openscad_colours))
col_themes.append(export_colour_theme("Black", "Black", "#17202A", openscad_colours))
col_themes.append(export_colour_theme("White", "White", "White", openscad_colours))
# RETURNS
# list[export_colour_theme] col_themes - colour themes with name, top colour, base colour
return col_themes
def input_colour_themes(colour_permutations = False, colour_option = None):
# FUNCTION
# get valid colour option using parameter, else user console input
# ARGUMENTS
# bool colour_permutations
# int colour_option
# ARGUMENT VALIDATION
_m = 'input_colour_themes'
av.val_bool(colour_permutations, 'colour_permutations', _m)
if not (av.val_int(colour_option, 'colour_option', _m, suppress_errors = True, suppress_console_outputs = True) or av.val_str(colour_option, 'colour_option', _m, suppress_errors = True, suppress_console_outputs = True) or str(type(colour_option)) == "<class 'NoneType'>"):
raise ValueError(av.error_msg_str(_m, 'colour_option', colour_option, "export_colour_theme identifier"))
# VARIABLE INSTANTIATION
colour_themes = gen_col_themes()
if colour_permutations: return colour_themes
n = len(colour_themes)
# METHODS
if not validate_input_colour(colour_option, colour_themes):
colour_table = PrettyTable(field_names=['index', 'name', 'colour top', 'colour base'])
for col_i in range(n):
colour_table.add_row([col_i + 1] + colour_themes[col_i].as_row()) # .insert(0, str(col_i + 1))
print(colour_table)
while not validate_input_colour(colour_option, colour_themes):
colour_option = input("Please input colour selection by name or index above.")
# RETURNS
return [get_colour_theme(colour_option, colour_themes)]
def validate_input_colour(colour_option, colour_themes):
# FUNCTION
# evaluate if colour_option relates to a colour_theme
# ARGUMENTS
# str/int colour_option
# list[export_colour_themes] colour_themes
# ARGUMENT VALIDATION
_m = 'validate_input_colour'
if not (av.val_int(colour_option, 'colour_option', _m, suppress_errors = True, suppress_console_outputs = True) or av.val_str(colour_option, 'colour_option', _m, suppress_errors = True, suppress_console_outputs = True)): return False
av.val_list(colour_themes, 'colour_themes', _m, "<class 'translate_braille_2_scad.export_colour_theme'>")
# VARIABLE INSTANTIATION
n = len(colour_themes)
# METHODS
if av.full_val_int(colour_option, 'colour_option', _m, suppress_errors = True, suppress_console_outputs = True):
colour_option = av.input_int(colour_option, 'colour_option', _m)
return (0 < colour_option and colour_option <= n)
for col_i in range(n):
if colour_option == colour_themes[col_i].name:
return True
# RETURNS
return False
def get_colour_theme(colour_option, colour_themes):
# FUNCTION
# get valid colour_option from colour_themes
# ARGUMENTS
# str/int colour_option
# list[export_colour_themes] colour_themes
# ARGUMENT VALIDATION
_m = 'get_colour_theme'
error_msg = av.error_msg_str(_m, colour_option, 'colour_option', 'export_colour_theme identifier')
if not (av.val_int(colour_option, 'colour_option', _m, suppress_errors = True, suppress_console_outputs = True) or av.val_str(colour_option, 'colour_option', _m, suppress_errors = True, suppress_console_outputs = True)):
raise ValueError(error_msg)
av.val_list(colour_themes, 'colour_themes', _m, "<class 'translate_braille_2_scad.export_colour_theme'>")
# VARIABLE INSTANTIATION
n = len(colour_themes)
# METHODS
if av.full_val_int(colour_option, 'colour_option', _m, suppress_errors = True, suppress_console_outputs = True):
colour_option = av.input_int(colour_option, 'colour_option', _m)
return colour_themes[colour_option - 1]
for col_i in range(n):
my_colour = colour_themes[col_i]
if colour_option == my_colour.name:
return my_colour
# ERROR HANDLING
raise ValueError(error_msg)
def gen_path_braille_scrabble(sz_scheme, col_theme, my_style, path_file = "C:\\Users\\edwar\\OneDrive\\Documents\\Programming\\Python Scripts\\Braille_Scrabble", suffix = "stl", presuffix=""):
# FUNCTION
# generate file path for braille scrabble design
# ARGUMENTS
# scrabble_dimensions sz_scheme
# int letter - index in the alphabet with base 0
# export_colour_theme col_theme
# style my_style
# string path_file - path of folder + filename (message text)
# string suffix - file type
# string presuffix - text before file type
# ARGUMENT VALIDATION
_m = 'gen_path_braille_scrabble'
av.val_type(sz_scheme, "<class 'translate_braille_2_scad.scrabble_dimensions'>", 'sz_scheme', _m)
av.val_type(col_theme, "<class 'translate_braille_2_scad.export_colour_theme'>", 'col_theme', _m)
av.val_type(my_style, "<class 'translate_braille_2_scad.style'>", 'my_style', _m)
av.val_str(path_file, 'path_file', _m)
av.val_str(suffix, 'suffix', _m)
av.val_str(presuffix, 'presuffix', _m)
# RETURNS
return f"{path_file}_{sz_scheme.name}_{col_theme.name}{my_style.gen_filetxt()}.{suffix}"
def gen_openscad_braille(cum, letters, numbers, braille, n_keys, n_row_keys, n_rows, mydims, mystyle, col_theme, filename, path_file="C:/Users/edwar/OneDrive/Documents/Programming/Python Scripts/Braille", quality = 25):
# FUNCTION
# generate openscad model of Braille product
# ARGUMENTS
# list[int] cum
# list[str] letters - array of plaintext for output
# list[int] numbers - ordinality of translated keys in alphabet / number line (where appropriate)
# list[list[int]] braille - translated keys
# int n_keys - number of keys in Braille message
# int n_row_keys - number of Braille keys per row on keyboard
# int n_rows - number of rows of keys on keyboard
# scrabble_dimensions mydims - sizing parameters
# style mystyle
# export_colour_themes col_theme
# str filename - excl. path
# str path_file
# ARGUMENT VALIDATION
_m = 'gen_openscad_braille'
av.val_list(cum, 'cum', _m, "<class 'int'>")
av.val_list(letters, 'letters', _m, "<class 'str'>")
av.val_nested_list(numbers, 0, 1, 'numbers', _m, "<class 'int'>")
av.val_nested_list(braille, 0, 2, 'braille', _m, "<class 'int'>", -1, [-1, -1, 6], -1, [-1, -1, 6])
av.val_int(n_keys, 'n_keys', _m)
av.val_int(n_row_keys, 'n_row_keys', _m)
av.val_int(n_rows, 'n_rows', _m)
# av.val_int(show_braille, 'show_braille', _m)
# av.val_int(show_let, 'show_let', _m)
# av.val_int(show_num, 'show_num', _m)
# av.val_type(show_braille, "<enum 'elem_visibility'>", 'show_braille', _m)
# av.val_type(show_let, "<enum 'elem_visibility'>", 'show_let', _m)
# av.val_type(show_num, "<enum 'elem_visibility'>", 'show_num', _m)
av.val_type(mystyle, "<class 'translate_braille_2_scad.style'>", 'mystyle', _m)
# if not (av.val_type(col_theme, "<class 'export_colour_theme'>", 'col_theme', _m, True) or av.val_type(col_theme, "<class 'translate_braille_2_scad.export_colour_theme'>", 'col_theme', _m, True)):
# raise ValueError(av.error_msg_str(_m, 'col_theme', col_theme, "<class 'export_colour_theme'>"))
av.val_type(col_theme, "<class 'translate_braille_2_scad.export_colour_theme'>", 'col_theme', _m)
av.val_str(filename, 'filename', _m)
av.val_str(path_file, 'path_file', _m)
# VARIABLE INSTANTIATION
# integer boolean conversions for position
i_s_braille = int(mystyle.braille == elem_visibility.VISIBLE)
i_s_let = int(mystyle.letter == elem_visibility.VISIBLE)
i_s_num = int(mystyle.number == elem_visibility.VISIBLE)
print(f'i_s_b = {i_s_braille}\ni_s_let = {i_s_let}\ni_s_num = {i_s_num}\n')
# dimensions
dp = mydims.dp # horizontal and vertical spacing between dots = 2.2 - 2.8mm
h_block = mydims.hblock # block height
s = mydims.s # inter-block spacing
h_base = mydims.base_height # thickness of base block
# derived from: horizontal distance between corresponding braille dots in adjacent cells = 5.1 - 6.8mm
# and also conforms to: vertical distance between corresponding braille dots in adjacent cells = 10 - 15mm
hcyl = mydims.hcyl # braille dot height
rcyl = mydims.rcyl #3/2; # braille dot base-radius
rsphere = mydims.rsphere #3/2*4/5; # braille dot top-radius
xb = dp + 2 * s + rcyl * 2
R_base_letter = [xb, xb] # alphabet block dimensions [x, y]
R_base_braille = [xb, xb + dp] # braille block dimensions [x, y]
R_base_number = np.array([xb, xb - 2 * s]) * i_s_num # number block dimensions [x, y] ' adjust to scalar coefficient rather than linear translation
htxt = hcyl # height of text
stxt = xb - 4 * s
snum = stxt * 0.75
ntxtsfn = 0.65 # scale factor for shrinking number text to fit more letters in single char space
ntxtsfm = 0.75 # scale factor for shrinking message text to fit more letters in single char space
font = '"Arial:style=Bold"'
_fn = quality
# keyboard layout
#maxln = 10;
#nln = min(maxln, N); # renamed n_row_keys
# temp = 1 + (N - 1) % nln
# yn = (N + nln - temp) / nln # - 0 ** (temp)
# print(f"number of rows = {yn}")
R_board = [n_row_keys * R_base_letter[0] + (n_row_keys + 3) * s,
n_rows * (R_base_braille[1] * i_s_braille + R_base_letter[1] * i_s_let + R_base_number[1] * i_s_num + s * (i_s_braille + i_s_let + i_s_num)) + 3 * s, # - 3 * s,
h_base] # +R_base_number[1]
print(f"R_board = {R_board}")
obj_dif = ops.Difference()
obj_uni = ops.Union()
# obj_uni.append(base_fillet_cube(R_board[0], R_board[1], R_board[2], h_block, centre = "false").translate([-2 * s - rcyl, 2 * s + rcyl - R_board[1], -h_block - R_board[2]]).color(col_theme.base))
obj_uni.append(ops.Cube([R_board[0], R_board[1], R_board[2]], h_block, centre = "false").translate([-3 * s - rcyl, 3 * s + rcyl - R_board[1], -h_block - R_board[2]]).color(col_theme.base))
# METHODS
for ab_i in range(len(braille)): # alpha-braille iterator
char = braille[ab_i]
print(f"char = braille[{ab_i}]") # " = {char}")
for char_j in range(len(char)):
char_old = [0, 0, 0, 0, 0, 0] if (ab_i == 0 and char_j == 0) else braille[0][char_j - 1] if (char_j > 0) else braille[ab_i - 1][len(braille[ab_i - 1]) - 1]
char_new = char[char_j]
print(f"char_new = {char_new}")
ab_n = (cum[ab_i] + char_j) % n_row_keys
ab_len = ((cum[ab_i] + char_j) - ab_n) / n_row_keys
ab_p = [ab_n * (R_base_braille[0] + s), -ab_len * (R_base_braille[1] * i_s_braille + R_base_letter[1] * i_s_let + R_base_number[1] * i_s_num + s * (i_s_braille + i_s_let + i_s_num)), 0]
# Bases
if (letters[ab_i] != " "):
if (mystyle.braille == elem_visibility.VISIBLE):
obj_uni.append(ops.Cube([R_base_braille[0], R_base_braille[1], h_block]).color(col_theme.base).translate([-(s + rcyl), s + rcyl - R_base_braille[1], -h_block]).translate(ab_p))
if (mystyle.letter == elem_visibility.VISIBLE):
obj_uni.append(ops.Cube([R_base_letter[0], R_base_letter[1], h_block]).color(col_theme.base).translate([-(s + rcyl), s + rcyl -R_base_letter[1], 0]).translate([0, -(R_base_braille[1] + s) * i_s_braille, -h_block]).translate(ab_p)) # should this be: rcyl - R_base_letter[1] ?????!?!?!?!!!!
# Braille message
# Dots
if (mystyle.braille == elem_visibility.VISIBLE):
for dot_y in range(3):
for dot_x in range(2):
if (char_new[dot_y + 3 * dot_x] == 1):
obj_uni.append(ops.Cylinder(hcyl, None, rcyl, rsphere, _fn = _fn, center = 'true').color(col_theme.top).translate([dot_x * dp, -dot_y * dp, 0]).translate(ab_p))
# Text
if (mystyle.letter == elem_visibility.VISIBLE):
obj_uni.append(ops.Text(f'"{letters[ab_i]}"', stxt * ntxtsfm ** (len(letters[ab_i]) - 1), font, halign = '"center"', valign = '"center"', _fn = _fn).linear_extrude(htxt, center = "false").translate([R_base_letter[0] / 2, R_base_letter[1] / 2, 0]).color(col_theme.top).translate([-(s + rcyl), s + rcyl -R_base_letter[1], 0]).translate([0, - (R_base_braille[1] + s) * i_s_braille, 0]).translate(ab_p))
# Text ordinality
num_new = numbers[ab_i]
if (num_new != [-1] and mystyle.number == elem_visibility.VISIBLE):
print(f"num_new = {num_new}, type = {type(num_new)}")
for num_x in range(len(num_new)):
snum = (R_base_number[1] - s * (len(num_new) + 1)) / (len(num_new) ** ntxtsfn)
# Base
obj_uni.append(ops.Cube([R_base_number[0], R_base_number[1], h_block]).color(col_theme.base).translate([0, -R_base_number[1], 0]).translate([-(s + rcyl), s + rcyl -(R_base_letter[1] + s) * i_s_let, 0]).translate([0, -(R_base_braille[1] + s) * i_s_braille, -h_block]).translate(ab_p))
# Number (text)
obj_uni.append(ops.Text(f'"{num_new[num_x]}"', snum, font, halign = '"center"', valign = '"center"', _fn = _fn).linear_extrude(htxt, center = "true").color(col_theme.top).translate([R_base_number[0] / 2 if (len(num_new) == 1) else R_base_number[0] / 2 + 1.6 * snum * (num_x - (len(num_new) - 1) / 2), R_base_number[1] * 0.25, htxt / 2]).translate([-s, s -R_base_number[1], 0]).translate([0, -(R_base_letter[1] - s) * i_s_let, 0]).translate([s-(s + rcyl), s - (R_base_braille[1] + s) * i_s_braille, -h_block * 0]).translate(ab_p))
obj_dif.append(obj_uni)
# remove excess plastic if no numbers on final row regardless of mystyle.number
remove_num_space = True
for ab_i in range(n_row_keys):
remove_num_space &= numbers[len(numbers) - ab_i - 1] == [-1]
# Trimming tools for no numbers:
if remove_num_space:
obj_dif.append(ops.Cube([R_board[0], R_base_number[1] + s, R_board[2]], center = "true").translate([R_board[0] / 2 - 3 * s - rcyl, 3 * s + rcyl + (R_base_number[1] + s) / 2 - R_board[1], -R_board[2] / 2 - h_block]))
# obj_dif.append(triprism(h_block, R[0], centre = "true").rotate([0, 90, 0]).translate([R[0] / 2 - 1.6, (12+4) * s - R[1], -1/1000 - R[2] - h_block]))
# embossed characters
for ab_i in range(len(braille)):
char = braille[ab_i]
print(f"char = braille[{ab_i}]") # " = {char}")
for char_j in range(len(char)):
# what is the purpose of char_old?
char_old = [0, 0, 0, 0, 0, 0] if (ab_i == 0 and char_j == 0) else braille[0][char_j - 1] if (char_j > 0) else braille[ab_i - 1][len(braille[ab_i - 1]) - 1]
char_new = char[char_j]
ab_n = (cum[ab_i] + char_j) % n_row_keys
ab_len = ((cum[ab_i] + char_j) - ab_n) / n_row_keys
ab_p = [ab_n * (R_base_braille[0] + s), -ab_len * (R_base_braille[1] * i_s_braille + R_base_letter[1] * i_s_let + R_base_number[1] * i_s_num + s * (i_s_braille + i_s_let + i_s_num)), 0]
# Text
if (mystyle.letter == elem_visibility.EMBOSSED):
obj_dif.append(ops.Text(f'"{letters[ab_i]}"', stxt * ntxtsfm ** (len(letters[ab_i]) - 1) * 1.15, font, halign = '"center"', valign = '"center"', _fn = _fn).linear_extrude(htxt, center = "false").mirror([1, 0, 0]).translate([R_base_letter[0] / 2, R_base_letter[1] * 3 / 4, - R_board[2]]).color(col_theme.top).translate([0, -R_base_braille[1], 0]).translate([-(s + rcyl), s + rcyl - (R_base_braille[1] + s) * i_s_braille * 0, -h_block]).translate(ab_p))
# my_prefix = '_0b' if (mystyle.braille == elem_visibility.HIDDEN) else ''
# my_prefix += '_0l' if (mystyle.letter == elem_visibility.HIDDEN) else ''
# my_prefix += '__l' if (mystyle.letter == elem_visibility.EMBOSSED) else ''
# my_prefix += '_0n' if (mystyle.number == elem_visibility.HIDDEN) else ''
# my_prefix = mystyle.gen_filetxt()
path_png = gen_path_braille_scrabble(mydims, col_theme, mystyle, path_file, 'png')
path_scad = gen_path_braille_scrabble(mydims, col_theme, mystyle, path_file, 'scad')
path_stl = gen_path_braille_scrabble(mydims, col_theme, mystyle, path_file, 'stl')
# RETURNS
obj_dif.write(path_scad)
print("writing SCAD")
# obj_dif.write(path_png)
return path_png, path_scad, path_stl
# def get_shownum(braille):
# shownum = 1
# for n_x in range(len(braille)):
# shownum &= not (not braille[n_x])
# return 0 # 1 if shownum else 0