Skip to content

Commit

Permalink
add travelele and turnaround
Browse files Browse the repository at this point in the history
  • Loading branch information
bat52 committed Jan 18, 2025
1 parent 361447b commit 9a94079
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 75 deletions.
52 changes: 51 additions & 1 deletion src/pylele/config_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ def __init__(
midTck: float = 10,
holeHt: float = 8,
tailAdj: float = -5,
topCutTck: float = 100,
code: str = 'P',
):
super().__init__(code = code)
Expand All @@ -100,6 +101,7 @@ def __init__(
self.btnRad = btnRad
self.midTck = midTck
self.tailAdj = tailAdj
self.topCutTck = topCutTck

def minGap(self) -> float:
return 2*max(self.majRad, self.btnRad) + .5
Expand Down Expand Up @@ -128,7 +130,6 @@ def tailAllow(self):
(_, back, _, _, _, _) = self.dims()
return back


class WormConfig(TunerConfig):

""" Worm Configuration """
Expand Down Expand Up @@ -204,6 +205,52 @@ def tailAllow(self):
(front, back, _, _, _, _) = self.dims()
return (front + back) / 2

class TurnaroundConfig(WormConfig):
def __init__(
self,
slitHt: float = 31,
slitLen: float = 10,
slitWth: float = 3,
diskTck: float = 5,
diskRad: float = 7.7,
axleRad: float = 3,
axleLen: float = 6, # original worm tuner has 8mm axle
driveRad: float = 4,
driveLen: float = 14,
driveOffset: float = 9.75,
gapAdj: float = 1,
tailAdj: float = 0,
buttonTck: float = 9.5,
buttonWth: float = 16,
buttonHt: float = 8,
buttonKeyLen: float = 6,
buttonKeyRad: float = 2.25,
buttonKeybaseRad: float = 3.8,
buttonKeybaseHt: float = 3,
code: str = 'T',
peg_config = PegConfig(topCutTck=25)
):
super().__init__(code = code)
self.slitHt = slitHt
self.slitLen = slitLen
self.slitWth = slitWth
self.diskTck = diskTck
self.diskRad = diskRad
self.axleRad = axleRad
self.axleLen = axleLen
self.driveRad = driveRad
self.driveLen = driveLen
self.driveOffset = driveOffset
self.gapAdj = gapAdj
self.tailAdj = tailAdj
self.buttonTck = buttonTck
self.buttonWth = buttonWth
self.buttonHt = buttonHt
self.buttonKeyRad = buttonKeyRad
self.buttonKeyLen = buttonKeyLen
self.buttonKeybaseRad = buttonKeybaseRad
self.buttonKeybaseHt = buttonKeybaseHt
self.peg_config = peg_config

FRICTION_PEG_CFG = PegConfig()
GOTOH_PEG_CFG = PegConfig(
Expand Down Expand Up @@ -232,12 +279,15 @@ def tailAllow(self):
code = 'B',
)

TURNAROUND_CFG = TurnaroundConfig()

class TunerType(Enum):
""" Tuner Type Enumerator """
FRICTION = FRICTION_PEG_CFG
GOTOH = GOTOH_PEG_CFG
WORM = WORM_TUNER_CFG
BIGWORM = BIGWORM_TUNER_CFG
TURNAROUND = TURNAROUND_CFG

def list()->list:
""" Return List of enumerated names """
Expand Down
3 changes: 2 additions & 1 deletion src/pylele/pylele2/all_assembly.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ def test_all_assembly(self, apis=None):
'worm' : '579205',
'flat' : '1078970',
'hollow' : '1053325',
'travel' : '1064942'
'travel' : '1064942',
'travelele' : '1064942'
}

test_config = {}
Expand Down
150 changes: 89 additions & 61 deletions src/pylele/pylele2/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ class LeleBodyType(StringEnum):
'worm' : WORM , # gourd
'flat' : WORM + ['-bt', LeleBodyType.FLAT] + TUNEBRIDGE,
'hollow' : BIGWORM + ['-bt', LeleBodyType.HOLLOW],
'travel' : BIGWORM + ['-bt', LeleBodyType.TRAVEL,'-wt','25'] + TUNEBRIDGE
'travel' : BIGWORM + ['-bt', LeleBodyType.TRAVEL,'-wt','25'] + TUNEBRIDGE,
'travelele' : ['-bt', LeleBodyType.TRAVEL,'-wt','25','-t','turnaround','-e','50'] + TUNEBRIDGE
}

class AttrDict(dict):
Expand All @@ -43,11 +44,6 @@ def __getattr__(self, key):
def __setattr__(self, key, value):
self[key] = value

def tzAdj(tY: float, tnrType: TunerType, endWth: float, top_ratio: float) -> float:
""" Adjust Tuner Z """
return 0 if tnrType.is_worm() or tY > endWth/2 \
else (((endWth/2)**2 - tY**2)**.5 * top_ratio/2 + .5)

def pylele_config_parser(parser = None):
"""
Pylele Base Element Parser
Expand Down Expand Up @@ -124,6 +120,10 @@ def pylele_config_parser(parser = None):

return parser

def tzAdj(tY: float, tnrType: TunerType, endWth: float, top_ratio: float) -> float:
""" Adjust Tuner Z """
return 0 if tnrType.is_worm() or tY > endWth/2 \
else (((endWth/2)**2 - tY**2)**.5 * top_ratio/2 + .5)

class LeleConfig:
""" Pylele Configuration Class """
Expand Down Expand Up @@ -159,6 +159,84 @@ def parse_args(self, args=None):

def is_odd_strs(self) -> bool:
return self.cli.num_strings % 2 == 1

def configure_tuners(self):
""" Configure Tuners
cli inputs:
scale_length
end_flat_width
tuner_type
num_string
parameter inputs:
self.bodyBackLen
self.bodyWth
self.extMidTopTck
self.TOP_RATIO
parameter outputs:
self.tnrXYZs
self.tZBase
"""

# Tuner config
# approx spline bout curve with ellipse but 'fatter'
scaleLen=float(self.cli.scale_length)
endWth = self.cli.end_flat_width
tnrType=TunerType[self.cli.tuner_type].value
tnrSetback = tnrType.tailAllow()
numStrs=self.cli.num_strings

tXMax = self.bodyBackLen - tnrSetback
fatRat = .7 + (endWth/self.bodyWth)/2
tYMax = fatRat*self.bodyWth - tnrSetback
tX = tXMax
tY = 0

if tnrType.is_peg():
tZBase = (self.extMidTopTck + 2)
elif tnrType.is_worm():
tZBase = (-tnrType.driveRad - tnrType.diskRad - tnrType.axleRad)
else:
assert False, f'Unsupported Tuner Type {tnrType}'
self.tZBase = tZBase

tMidZ = self.tZBase + tzAdj(tY, tnrType=tnrType, endWth=endWth, top_ratio=self.TOP_RATIO)
tZ = tMidZ
tDist = self.tnrGap
# start calc from middle out
self.tnrXYZs = [(scaleLen + tX, 0, tZ)] if self.is_odd_strs() else []
for p in range(numStrs//2):
if tY + tDist < endWth/2:
tY += tDist if self.is_odd_strs() or p > 0 else tDist/2
# tX remain same
tZ = self.tZBase + tzAdj(tY, tnrType=tnrType, endWth=endWth, top_ratio=self.TOP_RATIO)
else:
"""
Note: Ellipse points seperated by arc distance calc taken from
https://gamedev.stackexchange.com/questions/1692/what-is-a-simple-algorithm-for-calculating-evenly-distributed-points-on-an-ellip
view as the back of ukulele, which flips XY, diff from convention & post
X
^
|
b +-------._ (y,x)
| `@-._
| `-.
| `.
| \
-+--------------------+---> Y
O| a
y' = y + d / sqrt(1 + b²y² / (a²(a²-y²)))
x' = b sqrt(1 - y'²/a²)
"""
tY = tY + (tDist if self.is_odd_strs() or p > 0 else tDist/2) \
/ sqrt(1 + tXMax**2 * tY**2 / (tYMax**2 * (tYMax**2 - tY**2)))
tX = tXMax * sqrt(1 - tY**2/tYMax**2)
tZ = self.tZBase

self.tnrXYZs.extend(
[(scaleLen + tX, tY, tZ), (scaleLen + tX, -tY, tZ)],
)

def __init__(
self,
Expand Down Expand Up @@ -240,6 +318,9 @@ def __init__(
- self.brdgZ
self.brdgLen = nutStrGap

# Tuners Configuration
self.configure_tuners()

# Guide config (Only for Pegs)
self.guideHt = 6 + numStrs/2
self.guideX = scaleLen + .95*self.chmBack
Expand All @@ -259,60 +340,6 @@ def __init__(
gY += gGap
self.guideYs.extend([gY + gGapAdj, -gY -gGapAdj])

# Tuner config
# approx spline bout curve with ellipse but 'fatter'
tXMax = bodyBackLen - tnrSetback
fatRat = .7 + (endWth/self.bodyWth)/2
tYMax = fatRat*self.bodyWth - tnrSetback
tX = tXMax
tY = 0

if tnrType.is_peg():
tZBase = (self.extMidTopTck + 2)
elif tnrType.is_worm():
tZBase = (-tnrType.driveRad - tnrType.diskRad - tnrType.axleRad)
else:
assert False, f'Unsupported Tuner Type {tnrType}'

tMidZ = tZBase + tzAdj(tY, tnrType=tnrType, endWth=endWth, top_ratio=self.TOP_RATIO)
tZ = tMidZ
tDist = self.tnrGap
# start calc from middle out
self.tnrXYZs = [(scaleLen + tX, 0, tZ)] if self.is_odd_strs() else []
for p in range(numStrs//2):
if tY + tDist < endWth/2:
tY += tDist if self.is_odd_strs() or p > 0 else tDist/2
# tX remain same
tZ = tZBase + tzAdj(tY, tnrType=tnrType, endWth=endWth, top_ratio=self.TOP_RATIO)
else:
"""
Note: Ellipse points seperated by arc distance calc taken from
https://gamedev.stackexchange.com/questions/1692/what-is-a-simple-algorithm-for-calculating-evenly-distributed-points-on-an-ellip
view as the back of ukulele, which flips XY, diff from convention & post
X
^
|
b +-------._ (y,x)
| `@-._
| `-.
| `.
| \
-+--------------------+---> Y
O| a
y' = y + d / sqrt(1 + b²y² / (a²(a²-y²)))
x' = b sqrt(1 - y'²/a²)
"""
tY = tY + (tDist if self.is_odd_strs() or p > 0 else tDist/2) \
/ sqrt(1 + tXMax**2 * tY**2 / (tYMax**2 * (tYMax**2 - tY**2)))
tX = tXMax * sqrt(1 - tY**2/tYMax**2)
tZ = tZBase

self.tnrXYZs.extend(
[(scaleLen + tX, tY, tZ), (scaleLen + tX, -tY, tZ)],
)

# Strings config
strOddMidPath = [
(-self.headLen, 0, -self.FRETBD_SPINE_TCK - .2*self.SPINE_HT),
Expand All @@ -326,6 +353,7 @@ def __init__(
self.guideZ + self.guideHt - self.GUIDE_RAD - 1.5*self.STR_RAD)
)

tMidZ = 0 # temporary workaround
strOddMidPath.append(
(scaleLen + bodyBackLen - tnrSetback, 0,
tMidZ + tnrType.strHt())
Expand All @@ -342,7 +370,7 @@ def __init__(
strY = (self.tnrGap/2) if pt == strOddMidPath[-1] \
else nutStrGap/2 + pt[0]*tan(strEvenMidAng)
strZ = (
tZBase + tnrType.strHt()) if pt == strOddMidPath[-1] else pt[2]
self.tZBase + tnrType.strHt()) if pt == strOddMidPath[-1] else pt[2]
strEvenMidPathR.append((pt[0], strY, strZ))
strEvenMidPathL.append((pt[0], -strY, strZ))

Expand Down
10 changes: 7 additions & 3 deletions src/pylele/pylele2/peg.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@ class LelePeg(LeleBase):
def gen(self) -> Shape:
"""Generate Peg"""
cutAdj = FIT_TOL if self.isCut else 0
assert TunerType[self.cli.tuner_type].value.is_peg()
cfg: PegConfig = TunerType[self.cli.tuner_type].value
if TunerType[self.cli.tuner_type].value.is_peg():
cfg: PegConfig = TunerType[self.cli.tuner_type].value
elif TunerType[self.cli.tuner_type] == TunerType.TURNAROUND:
cfg: PegConfig = TunerType[self.cli.tuner_type].value.peg_config
else:
assert f"Unsupported Peg for tuner type {self.cli.tuner_type}"
joinTol = 2 * self.cfg.tolerance
strRad = self.cfg.STR_RAD + cutAdj
holeHt = cfg.holeHt
Expand All @@ -32,7 +36,7 @@ def gen(self) -> Shape:
midTck = cfg.midTck
botLen = cfg.botLen
btnRad = cfg.btnRad + cutAdj
topCutTck = 100 if self.isCut else 2 # big value for cutting
topCutTck = cfg.topCutTck if self.isCut else 2 # big value for cutting
botCutTck = botLen - midTck / 3 if self.isCut else 2

top = self.api.cylinder_z(topCutTck + joinTol, majRad).mv(0, 0, topCutTck / 2)
Expand Down
29 changes: 23 additions & 6 deletions src/pylele/pylele2/tuners.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
Pylele Tuners
"""

from math import ceil

import os
import sys

Expand All @@ -16,6 +18,7 @@
from pylele.pylele2.peg import LelePeg
from pylele.pylele2.worm import LeleWorm, pylele_worm_parser
from pylele.pylele2.worm_key import LeleWormKey
from pylele.pylele2.turnaround import LeleTurnaround


class LeleTuners(LeleBase):
Expand All @@ -28,26 +31,40 @@ def is_peg(self):
def is_worm(self):
tuners = TunerType[self.cli.tuner_type].value
return tuners.is_worm()

def gen(self) -> Shape:
"""Generate Tuners"""

tXYZs = self.cfg.tnrXYZs

tnrs = None
for txyz in tXYZs:
for txyz in self.cfg.tnrXYZs:
if self.is_peg():
tnr = LelePeg(isCut=self.isCut, cli=self.cli).gen_full()
else: # if tuners.is_worm():
tnr = LeleWorm(isCut=self.isCut, cli=self.cli).gen_full()
if self.cli.tuner_type == TunerType.TURNAROUND.name:
tnr = LeleTurnaround(isCut=self.isCut, cli=self.cli).gen_full()
else:
tnr = LeleWorm(isCut=self.isCut, cli=self.cli).gen_full()
# if not tnr is None:
tnr = tnr.mv(txyz[0], txyz[1], txyz[2])
tnrs = tnr + tnrs

# generate pegs for turnaround
if self.cli.tuner_type == TunerType.TURNAROUND.name:
ta_tnr = None
for i in range(ceil(self.cli.num_strings/2)):
tnr = LelePeg(isCut=self.isCut, cli=self.cli).gen_full()
peg_cfg = TunerType[self.cli.tuner_type].value.peg_config
tnr <<= (0,0,peg_cfg.midTck)
tnr.rotate_x(90).mv(self.cli.scale_length - 35 * (1 + i),
self.cfg.bodyWth/2,
-self.cli.flat_body_thickness/2)
ta_tnr = tnr + ta_tnr
ta_tnr += ta_tnr.mirror_and_join()
tnrs += ta_tnr

if self.is_worm() and self.cli.worm_has_key:
tnrs += LeleWormKey(cli=self.cli,isCut=self.isCut).gen_full()


return tnrs

def worm_fillet(self, top):
Expand Down
Loading

0 comments on commit 9a94079

Please sign in to comment.