import math
def augment_glyph(strokes):
  '''
  Takes a list of svg.path.Path objects that represent strokes and returns
  additional SVG elements that roughly correspond to a "median", a line that
  runs down the middle of the stroke.
  '''
  polygons = []
  medians = []
  for path in strokes:
    polygons.append(get_polygon_approximation(path, 32))
    medians.append(find_median(polygons[-1], 128))
  result = []
  for polygon in polygons:
    for point in polygon:
      result.append(
          ''.format(
              int(point.real), int(point.imag)))
  for median in medians:
    for point in median:
      result.append(
          ''.format(
              int(point.real), int(point.imag), 'black'))
  return result
def find_median(polygon, max_distance):
  result = []
  for i, point2 in enumerate(polygon):
    # For each polygon edge, we compute its midpoint and consider the portion
    # of its perpendicular bisector that extends into the polygon. We prepare
    # a few functions to compute dot products against this bisector:
    #   - dot measures which side of the bisector points are on. Note that
    #     dot(point) - dotmid is 0 if the point is on the perpendicular
    #     bisector, negative if it is on one side, and positive on the other.
    #   - sid measures whether the point is inside our outside the polygon.
    #     sid(point) - sidmid is 0 if the point is on this segment, positive
    #     if the point is within the polygon, and negative if it is outside.
    point1 = polygon[i - 1]
    midpoint = (point1 + point2)/2
    diff = point2 - point1
    dot = lambda point: diff.real*point.real + diff.imag*point.imag
    sid = lambda point: -diff.imag*point.real + diff.real*point.imag
    dotmid = dot(midpoint)
    sidmid = sid(midpoint)
    # For each other segment, we compute its intersection with the perpendicular
    # bisector and track the closest one overall.
    (best, best_distance, best_tangent) = (None, float('Inf'), None)
    for j, other2 in enumerate(polygon):
      if j == i:
        continue
      other1 = polygon[j - 1]
      (dot1, dot2) = (dot(other1) - dotmid, dot(other2) - dotmid)
      if dot1 == dot2 == 0:
        if abs(other1 - diff) > abs(other2 - diff):
          (other1, other2) = (other2, other1)
        intersection = other1 if dot1 == 0 else other2
      elif cmp(dot1, 0) == cmp(dot2, 0):
        continue
      else:
        t = dot1/(dot1 - dot2)
        intersection = (1 - t)*other1 + t*other2
      distance = abs(intersection - midpoint)
      if sid(intersection) > sidmid and distance < best_distance:
        tangent = other2 - other1
        (best, best_distance, best_tangent) = (intersection, distance, tangent)
    # If the perpendicular bisector intersects a segment opposite this one in
    # the polygon, we compute a point between the midpoint and the intersection
    # point as a candidate for our median line.
    #
    # We do NOT take (midpoint + best)/2. If this segment is not parallel to the
    # opposite segment, that point could be far from the median. Instead, we
    # compute the angle bisector of this segment and the opposte one and find
    # its intersection with the segment (midpoint, best).
    if best is None or best_distance > max_distance or not diff:
      continue
    ratio = best_tangent/diff
    cosine = abs(math.cos(math.atan2(ratio.imag, ratio.real)))
    t = cosine/(1 + cosine)
    result.append((1 - t)*midpoint + t*best)
  return result
def get_polygon_approximation(path, error):
  result = []
  for i, element in enumerate(path):
    num_interpolating_points = max(int(element.length()/error), 1)
    for i in xrange(num_interpolating_points):
      result.append(element.point(1.0*i/num_interpolating_points))
  return result