```#TODO: barbs are a little bit excessive.
import math
import vec
from .common import (
Ending,
slant45,
slant60,
BOTTOM,
UNDER,
)
from .util import hook

class BottomEnding(Ending):

pronunciation = NotImplemented

def __init__(self, character, straight, flipped):
super().__init__(character)
self.straight = straight
self.flipped = flipped

def predict_slant_width(self, pen, end_angle):
temp_pen = pen.copy()
temp_pen.line_to_y(BOTTOM, end_slant=end_angle)
return temp_pen.last_slant_width()

class Normal(BottomEnding):

pronunciation = ''

def angle(self):
if self.straight:
if not self.flipped:
return 45
else:
return -45
else:
return 0

def draw(self, pen):
pen.line_to_y(BOTTOM, end_slant=self.angle())

class Long(Normal):

pronunciation = 'l-'

angle = Normal.angle

def draw(self, pen):
pen.line_to_y(UNDER, end_slant=self.angle())

class DiagonalDownRightOnRight(BottomEnding):

pronunciation = 'm-'

def draw(self, pen):
slant_width = self.predict_slant_width(pen, 45)
pen.line_to_y(
BOTTOM + slant_width / slant45 / 2,
end_slant=45,
)

pen.turn_to(45)
pen.move_forward(pen.last_slant_width() / 2 + pen.mode.width / 2)

pen.turn_to(-45)
pen.line_to_y(BOTTOM, end_slant=0)

class DownOnRight(BottomEnding):

pronunciation = 'r-'

def draw(self, pen):
slant_width = self.predict_slant_width(pen, 45)
pen.line_to_y(
BOTTOM - slant_width / slant45 / 2,
end_slant=45,
)

pen.turn_to(45)
pen.move_forward(pen.last_slant_width() / 2 + pen.mode.width * slant45 / 2)

pen.turn_to(-90)
pen.line_forward(2.0, start_slant=45, end_slant=45)

class RightOnRight(BottomEnding):

pronunciation = 'l,-'

def draw(self, pen):
slant_width = self.predict_slant_width(pen, 45)
pen.line_to_y(
BOTTOM - slant_width / slant45 / 2,
end_slant=45,
)

pen.turn_to(45)
pen.move_to_y(BOTTOM + pen.mode.width / 2)
pen.turn_to(0)
pen.line_forward(2.0, start_slant=45, end_slant=45)

class DiagonalDownLeftOnRight(BottomEnding):

pronunciation = 'n-'

def draw(self, pen):
pen.line_to_y(BOTTOM, end_slant=0)
pen.turn_to(0)
pen.move_forward(pen.last_slant_width() / 2 + pen.mode.width * slant45 / 2)
pen.turn_to(-135)
pen.line_forward(2.0, start_slant=0, end_slant=0)

class Bend(BottomEnding):

pronunciation = ('s-', 'z-')

def draw(self, pen):
if self.straight:
pen.line_to_y(BOTTOM + pen.mode.width / 2)
pen.turn_to(0)
pen.line_forward(2.5, end_slant=-45)
else:
temp_pen = pen.copy()
temp_pen.line_to_y(BOTTOM)
temp_pen.turn_to(-90)
temp_pen.line_forward(2.0)
seg = temp_pen.last_segment()
offset_y = seg.a_right.y - BOTTOM

pen.line_to_y(BOTTOM - offset_y)
pen.turn_to(-90)
pen.line_forward(2.5, end_slant=45)

class Fold(BottomEnding):

pronunciation = ('s^-', 'z^-')

def draw(self, pen):
if self.straight:
pen.line_to_y(
BOTTOM + pen.mode.width / 2,
end_slant=-45,
)
pen.turn_to(-45)
pen.move_forward(pen.mode.width * slant45)
pen.turn_to(0)
pen.line_forward(2.0, start_slant=-45, end_slant=-45)
else:
slant_width = self.predict_slant_width(pen, 45)
pen.line_to_y(
BOTTOM + slant_width / slant45 / 2,
end_slant=45,
)
pen.turn_to(-135)
pen.move_forward(pen.last_slant_width() / 2 + pen.mode.width * slant45 / 2)
pen.turn_to(180)
pen.line_forward(2.0, start_slant=45, end_slant=45)

class Barb(BottomEnding):

pronunciation = 'n^-'

angle = Normal.angle

def draw(self, pen):
pen.line_to_y(BOTTOM + pen.mode.width / 4, end_slant=self.angle())

old_mode = pen.mode
new_mode = old_mode.copy()
new_mode.width = pen.mode.width / 2
pen.set_mode(new_mode)

if self.straight:
pen.turn_to(45)
pen.line_forward(2 * pen.mode.width)
seg = pen.last_segment()
seg.b_left = seg.a_left
else:
pen.turn_to(180)
pen.line_forward(2 * pen.mode.width)
seg = pen.last_segment()
seg.b_right = seg.a_right

pen.set_mode(old_mode)

class DiagonalUpRight(BottomEnding):

pronunciation = 'z-'

def draw(self, pen):
if self.straight:
pen.line_to_y(BOTTOM + pen.mode.width / 2, end_slant=-45)
pen.turn_to(-45)
pen.move_forward(pen.last_slant_width() / 2 + pen.mode.width / 2)
pen.turn_to(45)
pen.line_forward(2.0, start_slant=-45, end_slant=0)
else:
slant_width = self.predict_slant_width(pen, 90)
pen.line_to_y(
BOTTOM - slant_width / 2,
end_slant=90
)
pen.turn_to(90)
pen.move_to_y(pen.mode.width * slant60 / 2)
pen.turn_to(30)
pen.line_forward(2.0, start_slant=90, end_slant=90)

class Acute(BottomEnding):

pronunciation = 'r^-'

def straight_acute(self, pen, angle):
temp_pen = pen.copy()
temp_pen.line_to_y(BOTTOM)
temp_pen.turn_to(angle)
temp_pen.line_forward(2.0)
seg = temp_pen.last_segment()
offset_y = seg.a_right.y - BOTTOM

pen.line_to_y(BOTTOM - offset_y)
pen.turn_to(angle)

def draw(self, pen):
if self.straight:
self.straight_acute(pen, 30)
pen.line_forward(2.0, end_slant=90)
else:
pen.line_to_y(BOTTOM + pen.mode.width / 2)
pen.turn_to(180)
pen.line_forward(2.5, end_slant=-45)

class RightOnBottom(BottomEnding):

pronunciation = 'c,-'

def draw(self, pen):
slant_width = self.predict_slant_width(pen, 45)
pen.line_to_y(
BOTTOM + slant_width / slant45 / 2,
end_slant=45,
)
pen.turn_to(-135)
pen.move_to_y(BOTTOM - pen.mode.width / 2)
pen.turn_to(0)
pen.line_forward(3, start_slant=45, end_slant=45)

class Break(BottomEnding):

pronunciation = ('c-', 'dz-')

def draw(self, pen):
if self.straight:
pen.line_to_y(BOTTOM - pen.mode.width / 2, end_slant=-45)
pen.turn_to(135)
pen.move_forward(pen.mode.width * slant45)
pen.turn_to(-90)
pen.line_forward(2.0, start_slant=-45, end_slant=-45)
else:
pen.line_to_y(BOTTOM, end_slant=0)
pen.turn_to(180)
pen.move_forward(pen.last_slant_width())
pen.line_forward(2.0, start_slant=0, end_slant=0)

class BreakTurnLeft(BottomEnding):

pronunciation = ('c^-', 'j-')

def draw(self, pen):
if self.straight:
pen.line_to_y(BOTTOM, end_slant=0)
pen.turn_to(180)
pen.move_forward(pen.mode.width / 2 + pen.mode.width * slant45 / 2)
pen.turn_to(-45)
pen.line_forward(2.0, start_slant=0, end_slant=0)
else:
slant_width = self.predict_slant_width(pen, 90)
pen.line_to_y(
BOTTOM + slant_width / 2,
end_slant=90
)
pen.turn_to(-90)
pen.move_to_y(BOTTOM - pen.mode.width * slant60 / 2)
pen.turn_to(30)
pen.line_forward(2.0, start_slant=90, end_slant=90)

class BreakTurnRight(BottomEnding):

pronunciation = 'z^-'

def draw(self, pen):
if self.straight:
pen.line_to_y(BOTTOM - pen.mode.width / 2, end_slant=-45)
pen.turn_to(135)
pen.move_forward(pen.mode.width * slant45)
pen.turn_to(180)
pen.line_forward(2.0, start_slant=-45, end_slant=-45)
else:
pen.line_to_y(BOTTOM, end_slant=0)
pen.turn_to(180)
pen.move_forward(pen.last_slant_width() / 2 + pen.mode.width * slant45 / 2)
pen.turn_to(-135)
pen.line_forward(2.0, start_slant=0, end_slant=0)

class HookLeftOnRight(BottomEnding):

pronunciation = ('k-', 'g-')

def draw(self, pen):
slant_width = self.predict_slant_width(pen, 45)
pen.line_to_y(
BOTTOM + slant_width / slant45 / 2,
end_slant=45,
)
pen.turn_to(45)
pen.move_forward(slant_width / 2)

class HookRightOnRight(BottomEnding):

pronunciation = ('p-', 'b-')

def draw(self, pen):
slant_width = self.predict_slant_width(pen, 45)
pen.line_to_y(
BOTTOM + slant_width / slant45 / 2,
end_slant=45,
)
pen.turn_to(45)
pen.move_forward(slant_width / 2)

class FoldHookRight(BottomEnding):

pronunciation = 'q-'

def draw(self, pen):
if self.straight:
pen.line_to_y(
BOTTOM + pen.mode.width / 2,
end_slant=-45,
)
pen.turn_to(-45)
pen.move_forward(pen.mode.width * slant45 / 2)
else:
pen.line_to_y(BOTTOM, end_slant=0)
pen.turn_to(0)
pen.move_forward(pen.last_slant_width() / 2)

class FoldHookLeft(BottomEnding):

pronunciation = ('t-', 'd-')

def draw(self, pen):
if self.straight:
pen.line_to_y(
BOTTOM + pen.mode.width / 2,
end_slant=-45,
)
pen.turn_to(-45)
pen.move_forward(pen.mode.width * slant45 / 2)
else:
pen.line_to_y(BOTTOM, end_slant=0)
pen.turn_to(0)
pen.move_forward(pen.last_slant_width() / 2)

class TowardRightHookLeft(BottomEnding):

pronunciation = 'x-'

def draw(self, pen):
slant_width = self.predict_slant_width(pen, 45)
pen.line_to_y(
BOTTOM + slant_width / slant45 / 2,
end_slant=45,
)
pen.turn_to(-135)
pen.move_to_y(BOTTOM)

class TowardLeftHookLeft(BottomEnding):

pronunciation = ('t,-', 'dh-')

def draw(self, pen):
if self.straight:
pen.line_to_y(
BOTTOM + pen.mode.width / 2,
end_slant=-45,
)
pen.turn_to(-45)
pen.move_forward(pen.mode.width * slant45 / 2)
else:
pen.line_to_y(BOTTOM, end_slant=0)
pen.turn_to(0)
pen.move_forward(pen.last_slant_width() / 2)

class TowardLeftHookRight(BottomEnding):

pronunciation = ('f,-', 'v-')

def draw(self, pen):
if self.straight:
pen.line_to_y(
BOTTOM + pen.mode.width / 2,
end_slant=-45,
)
pen.turn_to(-45)
pen.move_forward(pen.mode.width * slant45 / 2)
else:
pen.line_to_y(BOTTOM, end_slant=0)
pen.turn_to(0)
pen.move_forward(pen.last_slant_width() / 2)

class TowardRightHookRight(BottomEnding):

pronunciation = 'xh-'

def draw(self, pen):
slant_width = self.predict_slant_width(pen, 45)
pen.line_to_y(
BOTTOM + slant_width / slant45 / 2,
end_slant=45,
)
pen.turn_to(-135)
pen.move_to_y(BOTTOM)

class BendBreak(BottomEnding):

pronunciation = 'GEMINATE'

def draw(self, pen):
if self.straight:
pen.line_to_y(BOTTOM + pen.mode.width / 2)
pen.turn_to(0)
pen.line_forward(2.0, end_slant=45)
pen.turn_to(-135)
pen.move_forward(pen.mode.width * slant45)
pen.turn_to(0)
pen.line_forward(2.0, start_slant=45, end_slant=45)
else:
pen.line_to_y(BOTTOM + slant45, end_slant=0)
pen.turn_to(-135)
pen.line_to_y(BOTTOM, end_slant=0)
pen.turn_to(0)
pen.move_forward(pen.mode.width * slant45)
pen.turn_to(-135)
pen.line_forward(2.0, start_slant=0, end_slant=0)

class Scythe(BottomEnding):

pronunciation = '-w'

def draw(self, pen):
#TODO: this is not very good.

def scythe_cap(pen, end):

switch = False
if self.character.mirrored_x:
switch = not switch
if self.flipped:
switch = not switch

if not switch:
top = end
bottom = pen.position
else:
top = pen.position
bottom = end

# Trace the curves with a temporary pen.
temp_pen = pen.copy()
if not switch:
arc = temp_pen.arc_right
else:
arc = temp_pen.arc_left

temp_pen.move_to(top)
outer_arcs = [
(2.4, 1.6),
(1.0, 2.8),
]
outer_points = []
circumference = radius * 2 * math.pi
circle_ratio = distance / circumference
angle = circle_ratio * 360
outer_points.append(temp_pen.position)

temp_pen.move_to(bottom)
temp_pen.move_forward(0.5)
inner_forward = temp_pen.position
temp_pen.arc_to(outer_points[-1])

# Draw with the real pen.
if not switch:
pen.line_to(inner_forward)
pen.arc_to(outer_points[-1])
pen.turn_to(outer_tip_angle + 180)
for p in reversed(outer_points[:-1]):
pen.arc_to(p)
pen.arc_to(top)
else:
for p in outer_points:
pen.arc_to(p)
pen.turn_to(inner_tip_angle + 180)
pen.arc_to(inner_forward)
pen.line_to(bottom)

pen.line_to_y(BOTTOM + pen.mode.width / 2)
pen.turn_to(0)

# See how far forward we have to go to make the top of the stroke
# zero-length.
temp_pen = pen.copy(paper=True)
temp_pen.line_forward(pen.mode.width, end_slant=90)
seg = temp_pen.last_segment()
extra_left = vec.mag(vec.vfrom(seg.a_left, seg.b_left))
extra_right = vec.mag(vec.vfrom(seg.a_right, seg.b_right))
extra = min(extra_left, extra_right)
dist = pen.mode.width - extra
pen.line_forward(dist, end_slant=90)
pen.last_segment().end_cap = scythe_cap

class AcuteFold(Acute):

pronunciation = '-y'

def draw(self, pen):
if self.straight:
self.straight_acute(pen, 30)
pen.line_forward(2.0, end_slant=-45)
pen.turn_to(-45)
pen.move_forward(pen.last_slant_width() / 2 + pen.mode.width / slant60)
pen.turn_to(-90)
pen.line_forward(2.0, start_slant=-45, end_slant=-45)
else:
pen.line_to_y(BOTTOM + pen.mode.width / 2)
pen.turn_to(180)
pen.line_forward(3.0, end_slant=45)
pen.turn_to(-135)
pen.move_forward(pen.mode.width * slant45)
pen.turn_to(-90)
pen.line_forward(2.0, start_slant=45, end_slant=45)

class BendBarb(BottomEnding):

pronunciation = '-l'

def draw(self, pen):
if self.straight:
pen.line_to_y(BOTTOM + pen.mode.width / 2, end_slant=0)
pen.turn_to(0)
pen.line_forward(2.5, end_slant=45)

else:
temp_pen = pen.copy()
temp_pen.line_to_y(BOTTOM, end_slant=0)
temp_pen.turn_to(-90)
temp_pen.line_forward(2.0)
seg = temp_pen.last_segment()
offset_y = seg.a_right.y - BOTTOM

pen.line_to_y(BOTTOM - offset_y)
pen.turn_to(-90)
pen.line_forward(2.5, end_slant=45)

old_mode = pen.mode
new_mode = old_mode.copy()
new_mode.width = pen.mode.width / 2
pen.set_mode(new_mode)

if self.straight:
pen.turn_to(135)
pen.line_forward(2 * pen.mode.width)
seg = pen.last_segment()
seg.b_left = seg.a_left
else:
pen.turn_to(45)
pen.line_forward(2 * pen.mode.width)
seg = pen.last_segment()
seg.b_left = seg.a_left

pen.set_mode(old_mode)

class AcuteBarb(Acute):

pronunciation = '-r'

def draw(self, pen):
if self.straight:
self.straight_acute(pen, 30)
pen.line_forward(2.0, end_slant=90)
else:
pen.line_to_y(BOTTOM + pen.mode.width / 2)
pen.turn_to(180)
pen.line_forward(2.5, end_slant=-45)

old_mode = pen.mode
new_mode = old_mode.copy()
new_mode.width = pen.mode.width / 2
pen.set_mode(new_mode)

if self.straight:
pen.turn_to(-90)
pen.line_forward(2 * pen.mode.width)
seg = pen.last_segment()
seg.b_right = seg.a_right
else:
pen.turn_to(-45)
pen.line_forward(2 * pen.mode.width)
seg = pen.last_segment()
seg.b_left = seg.a_left

pen.set_mode(old_mode)

class AcuteBreak(Acute):

pronunciation = '-r^'

def draw(self, pen):
if self.straight:
self.straight_acute(pen, 30)
pen.line_forward(2.0, end_slant=90)
pen.turn_to(-90)
pen.move_forward(pen.last_slant_width())
pen.turn_to(30)
pen.line_forward(2.0, start_slant=90, end_slant=90)
else:
pen.line_to_y(BOTTOM + pen.mode.width / 2)
pen.turn_to(180)
pen.line_forward(2.0, end_slant=-45)
pen.turn_to(-45)
pen.move_forward(pen.mode.width * slant45)
pen.turn_to(180)
pen.line_forward(2.0, start_slant=-45, end_slant=-45)

class AcuteLineHigh(Acute):

pronunciation = '-m'

def draw(self, pen):
if self.straight:
self.straight_acute(pen, 22.5)
pen.line_to_x(pen.position.x - pen.mode.width / 2 + x, end_slant=90)
pen.turn_to(-90)
pen.move_to_y(BOTTOM + pen.mode.width / 2)
pen.turn_to(0)
pen.line_forward(2.0, start_slant=90, end_slant=-45)
else:
pen.line_to_y(BOTTOM + pen.mode.width / 2)
pen.turn_to(180)
pen.line_forward(2.0, end_slant=-45)
pen.turn_to(-45)
pen.move_forward(pen.mode.width * slant45 / 2 + pen.mode.width / 2)
pen.turn_to(-135)
pen.line_forward(2.0, start_slant=-45, end_slant=90)

class AcuteLineLow(Acute):

pronunciation = '-n'

def draw(self, pen):
if self.straight:
self.straight_acute(pen, 30)
pen.line_to_x(pen.position.x - pen.mode.width / 2 + x, end_slant=90)
pen.turn_to(-90)
pen.move_to_y(BOTTOM + pen.mode.width * slant60 / 2)
pen.turn_to(-30)
pen.line_forward(2.0, start_slant=90, end_slant=90)
else:
pen.line_to_y(BOTTOM + pen.mode.width / 2)
pen.turn_to(180)
pen.line_forward(2.0, end_slant=-45)
pen.turn_to(-45)
pen.move_forward(pen.mode.width * slant45)
pen.turn_to(-90)
pen.line_forward(2.0, start_slant=-45, end_slant=-45)

class DoubleBend(Acute):

pronunciation = '-v'

def draw(self, pen):
if self.straight:
self.straight_acute(pen, 22.5)
pen.line_forward(1.5)
pen.turn_to(-22.5)
slant_width = self.predict_slant_width(pen, 90)
pen.line_to_y(BOTTOM + slant_width / 2, end_slant=90)
else:
pen.line_to_y(BOTTOM + pen.mode.width / 2)
pen.turn_to(180)
pen.line_forward(2.0)
pen.line_to_y(BOTTOM - 1.0, end_slant=0)

class BreakSlightRight(BottomEnding):

pronunciation = '-n^'

def draw(self, pen):
if self.straight:
slant_width = self.predict_slant_width(pen, -45)
pen.line_to_y(
BOTTOM + slant_width / slant45 / 2,
end_slant=-45,
)

pen.turn_to(135)
pen.move_forward(pen.last_slant_width() / 2 + pen.mode.width / 2)
pen.turn_to(-135)
pen.line_to_y(BOTTOM, end_slant=0)
else:
pen.line_to_y(BOTTOM, end_slant=0)
pen.turn_to(180)
pen.move_forward(pen.last_slant_width() / 2 + pen.mode.width / 2)
pen.turn_to(-90)
pen.line_forward(2.0, start_slant=0, end_slant=-45)

bottom_endings = [
Normal,
Long,
DiagonalDownRightOnRight,
DownOnRight,
RightOnRight,
DiagonalDownLeftOnRight,
Bend,
Fold,
Barb,
DiagonalUpRight,
Acute,
RightOnBottom,
Break,
BreakTurnLeft,
BreakTurnRight,
HookLeftOnRight,
HookRightOnRight,
FoldHookRight,
FoldHookLeft,
TowardRightHookLeft,
TowardLeftHookLeft,
TowardLeftHookRight,
TowardRightHookRight,
BendBreak,
Scythe,
AcuteFold,
BendBarb,
AcuteBarb,
AcuteBreak,
AcuteLineHigh,
AcuteLineLow,
DoubleBend,
BreakSlightRight,
]

class BottomAll(BottomEnding):
"""
A debug ending showing every one of the endings superimposed.
"""
def draw(self, pen):
start_position = pen.position
for bottom_ending_class in bottom_endings:
if bottom_ending_class == BottomAll:
continue
bottom_ending = bottom_ending_class(
self.character,
self.straight,
self.flipped,
)
pen.move_to(start_position)