Add Braille example
This commit is contained in:
parent
49823b9d45
commit
6fc456bca6
183
CadQuery/Examples/Ex029_Braille.py
Normal file
183
CadQuery/Examples/Ex029_Braille.py
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import unicode_literals, division
|
||||||
|
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
import cadquery as cq
|
||||||
|
from Helpers import show
|
||||||
|
|
||||||
|
# text_lines is a list of text lines.
|
||||||
|
# FreeCAD in braille (converted with braille-converter:
|
||||||
|
# https://github.com/jpaugh/braille-converter.git).
|
||||||
|
text_lines = ['⠠ ⠋ ⠗ ⠑ ⠑ ⠠ ⠉ ⠠ ⠁ ⠠ ⠙']
|
||||||
|
# See http://www.tiresias.org/research/reports/braille_cell.htm for examples
|
||||||
|
# of braille cell geometry.
|
||||||
|
horizontal_interdot = 2.5
|
||||||
|
vertical_interdot = 2.5
|
||||||
|
horizontal_intercell = 6
|
||||||
|
vertical_interline = 10
|
||||||
|
dot_height = 0.5
|
||||||
|
dot_diameter = 1.3
|
||||||
|
|
||||||
|
base_thickness = 1.5
|
||||||
|
|
||||||
|
# End of configuration.
|
||||||
|
BrailleCellGeometry = namedtuple('BrailleCellGeometry',
|
||||||
|
('horizontal_interdot',
|
||||||
|
'vertical_interdot',
|
||||||
|
'intercell',
|
||||||
|
'interline',
|
||||||
|
'dot_height',
|
||||||
|
'dot_diameter'))
|
||||||
|
|
||||||
|
|
||||||
|
class Point(object):
|
||||||
|
def __init__(self, x, y):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
|
||||||
|
def __add__(self, other):
|
||||||
|
return Point(self.x + other.x, self.y + other.y)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return 2
|
||||||
|
|
||||||
|
def __getitem__(self, index):
|
||||||
|
return (self.x, self.y)[index]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return '({}, {})'.format(self.x, self.y)
|
||||||
|
|
||||||
|
|
||||||
|
def brailleToPoints(text, cell_geometry):
|
||||||
|
# Unicode bit pattern (cf. https://en.wikipedia.org/wiki/Braille_Patterns).
|
||||||
|
mask1 = 0b00000001
|
||||||
|
mask2 = 0b00000010
|
||||||
|
mask3 = 0b00000100
|
||||||
|
mask4 = 0b00001000
|
||||||
|
mask5 = 0b00010000
|
||||||
|
mask6 = 0b00100000
|
||||||
|
mask7 = 0b01000000
|
||||||
|
mask8 = 0b10000000
|
||||||
|
masks = (mask1, mask2, mask3, mask4, mask5, mask6, mask7, mask8)
|
||||||
|
|
||||||
|
# Corresponding dot position
|
||||||
|
w = cell_geometry.horizontal_interdot
|
||||||
|
h = cell_geometry.vertical_interdot
|
||||||
|
pos1 = Point(0, 2 * h)
|
||||||
|
pos2 = Point(0, h)
|
||||||
|
pos3 = Point(0, 0)
|
||||||
|
pos4 = Point(w, 2 * h)
|
||||||
|
pos5 = Point(w, h)
|
||||||
|
pos6 = Point(w, 0)
|
||||||
|
pos7 = Point(0, -h)
|
||||||
|
pos8 = Point(w, -h)
|
||||||
|
pos = (pos1, pos2, pos3, pos4, pos5, pos6, pos7, pos8)
|
||||||
|
|
||||||
|
# Braille blank pattern (u'\u2800').
|
||||||
|
blank = '⠀'
|
||||||
|
points = []
|
||||||
|
# Position of dot1 along the x-axis (horizontal).
|
||||||
|
character_origin = 0
|
||||||
|
for c in text:
|
||||||
|
for m, p in zip(masks, pos):
|
||||||
|
delta_to_blank = ord(c) - ord(blank)
|
||||||
|
if (m & delta_to_blank):
|
||||||
|
points.append(p + Point(character_origin, 0))
|
||||||
|
character_origin += cell_geometry.intercell
|
||||||
|
return points
|
||||||
|
|
||||||
|
|
||||||
|
def get_plate_height(text_lines, cell_geometry):
|
||||||
|
# cell_geometry.vertical_interdot is also used as space between base
|
||||||
|
# borders and characters.
|
||||||
|
return (2 * cell_geometry.vertical_interdot +
|
||||||
|
2 * cell_geometry.vertical_interdot +
|
||||||
|
(len(text_lines) - 1) * cell_geometry.interline)
|
||||||
|
|
||||||
|
|
||||||
|
def get_plate_width(text_lines, cell_geometry):
|
||||||
|
# cell_geometry.horizontal_interdot is also used as space between base
|
||||||
|
# borders and characters.
|
||||||
|
max_len = max([len(t) for t in text_lines])
|
||||||
|
return (2 * cell_geometry.horizontal_interdot +
|
||||||
|
cell_geometry.horizontal_interdot +
|
||||||
|
(max_len - 1) * cell_geometry.intercell)
|
||||||
|
|
||||||
|
|
||||||
|
def get_cylinder_radius(cell_geometry):
|
||||||
|
"""Return the radius the cylinder should have
|
||||||
|
|
||||||
|
The cylinder have the same radius as the half-sphere make the dots (the
|
||||||
|
hidden and the shown part of the dots).
|
||||||
|
The radius is such that the spherical cap with diameter
|
||||||
|
cell_geometry.dot_diameter has a height of cell_geometry.dot_height.
|
||||||
|
"""
|
||||||
|
h = cell_geometry.dot_height
|
||||||
|
r = cell_geometry.dot_diameter / 2
|
||||||
|
return (r ** 2 + h ** 2) / 2 / h
|
||||||
|
|
||||||
|
|
||||||
|
def get_base_plate_thickness(plate_thickness, cell_geometry):
|
||||||
|
"""Return the height on which the half spheres will sit"""
|
||||||
|
return (plate_thickness +
|
||||||
|
get_cylinder_radius(cell_geometry) -
|
||||||
|
cell_geometry.dot_height)
|
||||||
|
|
||||||
|
|
||||||
|
def make_base(text_lines, cell_geometry, plate_thickness):
|
||||||
|
base_width = get_plate_width(text_lines, cell_geometry)
|
||||||
|
base_height = get_plate_height(text_lines, cell_geometry)
|
||||||
|
base_thickness = get_base_plate_thickness(plate_thickness, cell_geometry)
|
||||||
|
base = cq.Workplane('XY').box(base_width, base_height, base_thickness,
|
||||||
|
centered=(False, False, False))
|
||||||
|
return base
|
||||||
|
|
||||||
|
|
||||||
|
def make_embossed_plate(text_lines, cell_geometry):
|
||||||
|
"""Make an embossed plate with dots as spherical caps
|
||||||
|
|
||||||
|
Method:
|
||||||
|
- make a thin plate on which sit cylinders
|
||||||
|
- fillet the upper edge of the cylinders so to get pseudo half-spheres
|
||||||
|
- make the union with a thicker plate so that only the sphere caps stay
|
||||||
|
"visible".
|
||||||
|
"""
|
||||||
|
base = make_base(text_lines, cell_geometry, base_thickness)
|
||||||
|
|
||||||
|
dot_pos = []
|
||||||
|
base_width = get_plate_width(text_lines, cell_geometry)
|
||||||
|
base_height = get_plate_height(text_lines, cell_geometry)
|
||||||
|
y = base_height - 3 * cell_geometry.vertical_interdot
|
||||||
|
line_start_pos = Point(cell_geometry.horizontal_interdot, y)
|
||||||
|
for text in text_lines:
|
||||||
|
dots = brailleToPoints(text, cell_geometry)
|
||||||
|
dots = [p + line_start_pos for p in dots]
|
||||||
|
dot_pos += dots
|
||||||
|
line_start_pos += Point(0, -cell_geometry.interline)
|
||||||
|
|
||||||
|
r = get_cylinder_radius(cell_geometry)
|
||||||
|
base = base.faces('>Z').vertices('<XY').workplane() \
|
||||||
|
.pushPoints(dot_pos).circle(r) \
|
||||||
|
.extrude(r)
|
||||||
|
# Make a fillet almost the same radius to get a pseudo spherical cap.
|
||||||
|
base = base.faces('>Z').edges() \
|
||||||
|
.fillet(r - 0.001)
|
||||||
|
hidding_box = cq.Workplane('XY').box(
|
||||||
|
base_width, base_height, base_thickness, centered=(False, False, False))
|
||||||
|
result = hidding_box.union(base)
|
||||||
|
return result
|
||||||
|
|
||||||
|
_cell_geometry = BrailleCellGeometry(
|
||||||
|
horizontal_interdot,
|
||||||
|
vertical_interdot,
|
||||||
|
horizontal_intercell,
|
||||||
|
vertical_interline,
|
||||||
|
dot_height,
|
||||||
|
dot_diameter)
|
||||||
|
|
||||||
|
if base_thickness < get_cylinder_radius(_cell_geometry):
|
||||||
|
raise ValueError('Base thickness should be at least {}'.format(dot_height))
|
||||||
|
|
||||||
|
show(make_embossed_plate(text_lines, _cell_geometry))
|
Loading…
Reference in New Issue
Block a user