text: """This submodule contains functions related to smoothing paths of Bezier
  curves."""
  
  # External Dependencies
  from __future__ import division, absolute_import, print_function
  
  # Internal Dependencies
  from .path import Path, CubicBezier, Line
  from .misctools import isclose
  from .paths2svg import disvg
  
  
  def is_differentiable(path, tol=1e-8):
      for idx in range(len(path)):
          u = path[(idx-1) % len(path)].unit_tangent(1)
          v = path[idx].unit_tangent(0)
          u_dot_v = u.real*v.real + u.imag*v.imag
          if abs(u_dot_v - 1) > tol:
              return False
      return True
  
  
  def kinks(path, tol=1e-8):
      """returns indices of segments that start on a non-differentiable joint."""
      kink_list = []
      for idx in range(len(path)):
          if idx == 0 and not path.isclosed():
              continue
          try:
              u = path[(idx - 1) % len(path)].unit_tangent(1)
              v = path[idx].unit_tangent(0)
              u_dot_v = u.real*v.real + u.imag*v.imag
              flag = False
          except ValueError:
              flag = True
  
          if flag or abs(u_dot_v - 1) > tol:
              kink_list.append(idx)
      return kink_list
  
  
  def _report_unfixable_kinks(_path, _kink_list):
      mes = ("\n%s kinks have been detected at that cannot be smoothed.\n"
             "To ignore these kinks and fix all others, run this function "
             "again with the second argument 'ignore_unfixable_kinks=True' "
             "The locations of the unfixable kinks are at the beginnings of "
             "segments: %s" % (len(_kink_list), _kink_list))
      disvg(_path, nodes=[_path[idx].start for idx in _kink_list])
      raise Exception(mes)
  
  
  def smoothed_joint(seg0, seg1, maxjointsize=3, tightness=1.99):
      """ See Andy's notes on
      Smoothing Bezier Paths for an explanation of the method.
      Input: two segments seg0, seg1 such that seg0.end==seg1.start, and
      jointsize, a positive number
  
      Output: seg0_trimmed, elbow, seg1_trimmed, where elbow is a cubic bezier
          object that smoothly connects seg0_trimmed and seg1_trimmed.
  
      """
      assert seg0.end == seg1.start
      assert 0 < maxjointsize
      assert 0 < tightness < 2
  #    sgn = lambda x:x/abs(x)
      q = seg0.end
  
      try: v = seg0.unit_tangent(1)
      except: v = seg0.unit_tangent(1 - 1e-4)
      try: w = seg1.unit_tangent(0)
      except: w = seg1.unit_tangent(1e-4)
  
      max_a = maxjointsize / 2
      a = min(max_a, min(seg1.length(), seg0.length()) / 20)
      if isinstance(seg0, Line) and isinstance(seg1, Line):
          '''
          Note: Letting
              c(t) = elbow.point(t), v= the unit tangent of seg0 at 1, w = the
              unit tangent vector of seg1 at 0,
              Q = seg0.point(1) = seg1.point(0), and a,b>0 some constants.
              The elbow will be the unique CubicBezier, c, such that
              c(0)= Q-av, c(1)=Q+aw, c'(0) = bv, and c'(1) = bw
              where a and b are derived above/below from tightness and
              maxjointsize.
          '''
  #        det = v.imag*w.real-v.real*w.imag
          # Note:
          # If det is negative, the curvature of elbow is negative for all
          # real t if and only if b/a > 6
          # If det is positive, the curvature of elbow is negative for all
          # real t if and only if b/a < 2
  
  #        if det < 0:
  #            b = (6+tightness)*a
  #        elif det > 0:
  #            b = (2-tightness)*a
  #        else:
  #            raise Exception("seg0 and seg1 are parallel lines.")
          b = (2 - tightness)*a
          elbow = CubicBezier(q - a*v, q - (a - b/3)*v, q + (a - b/3)*w, q + a*w)
          seg0_trimmed = Line(seg0.start, elbow.start)
          seg1_trimmed = Line(elbow.end, seg1.end)
          return seg0_trimmed, [elbow], seg1_trimmed
      elif isinstance(seg0, Line):
          '''
          Note: Letting
              c(t) = elbow.point(t), v= the unit tangent of seg0 at 1,
              w = the unit tangent vector of seg1 at 0,
              Q = seg0.point(1) = seg1.point(0), and a,b>0 some constants.
              The elbow will be the unique CubicBezier, c, such that
              c(0)= Q-av, c(1)=Q, c'(0) = bv, and c'(1) = bw
              where a and b are derived above/below from tightness and
              maxjointsize.
          '''
  #        det = v.imag*w.real-v.real*w.imag
          # Note: If g has the same sign as det, then the curvature of elbow is
          # negative for all real t if and only if b/a < 4
          b = (4 - tightness)*a
  #        g = sgn(det)*b
          elbow = CubicBezier(q - a*v, q + (b/3 - a)*v, q - b/3*w, q)
          seg0_trimmed = Line(seg0.start, elbow.start)
          return seg0_trimmed, [elbow], seg1
      elif isinstance(seg1, Line):
          args = (seg1.reversed(), seg0.reversed(), maxjointsize, tightness)
          rseg1_trimmed, relbow, rseg0 = smoothed_joint(*args)
          elbow = relbow[0].reversed()
          return seg0, [elbow], rseg1_trimmed.reversed()
      else:
          # find a point on each seg that is about a/2 away from joint.  Make
          # line between them.
          t0 = seg0.ilength(seg0.length() - a/2)
          t1 = seg1.ilength(a/2)
          seg0_trimmed = seg0.cropped(0, t0)
          seg1_trimmed = seg1.cropped(t1, 1)
          seg0_line = Line(seg0_trimmed.end, q)
          seg1_line = Line(q, seg1_trimmed.start)
  
          args = (seg0_trimmed, seg0_line, maxjointsize, tightness)
          dummy, elbow0, seg0_line_trimmed = smoothed_joint(*args)
  
          args = (seg1_line, seg1_trimmed, maxjointsize, tightness)
          seg1_line_trimmed, elbow1, dummy = smoothed_joint(*args)
  
          args = (seg0_line_trimmed, seg1_line_trimmed, maxjointsize, tightness)
          seg0_line_trimmed, elbowq, seg1_line_trimmed = smoothed_joint(*args)
  
          elbow = elbow0 + [seg0_line_trimmed] + elbowq + [seg1_line_trimmed] + elbow1
          return seg0_trimmed, elbow, seg1_trimmed
  
  
  def smoothed_path(path, maxjointsize=3, tightness=1.99, ignore_unfixable_kinks=False):
      """returns a path with no non-differentiable joints."""
      if len(path) == 1:
          return path
  
      assert path.iscontinuous()
  
      sharp_kinks = []
      new_path = [path[0]]
      for idx in range(len(path)):
          if idx == len(path)-1:
              if not path.isclosed():
                  continue
              else:
                  seg1 = new_path[0]
          else:
              seg1 = path[idx + 1]
          seg0 = new_path[-1]
  
          try:
              unit_tangent0 = seg0.unit_tangent(1)
              unit_tangent1 = seg1.unit_tangent(0)
              flag = False
          except ValueError:
              flag = True  # unit tangent not well-defined
  
          if not flag and isclose(unit_tangent0, unit_tangent1):  # joint is already smooth
              if idx != len(path)-1:
                  new_path.append(seg1)
              continue
          else:
              kink_idx = (idx + 1) % len(path)  # kink at start of this seg
              if not flag and isclose(-unit_tangent0, unit_tangent1):
                  # joint is sharp 180 deg (must be fixed manually)
                  new_path.append(seg1)
                  sharp_kinks.append(kink_idx)
              else:  # joint is not smooth, let's  smooth it.
                  args = (seg0, seg1, maxjointsize, tightness)
                  new_seg0, elbow_segs, new_seg1 = smoothed_joint(*args)
                  new_path[-1] = new_seg0
                  new_path += elbow_segs
                  if idx == len(path) - 1:
                      new_path[0] = new_seg1
                  else:
                      new_path.append(new_seg1)
  
      # If unfixable kinks were found, let the user know
      if sharp_kinks and not ignore_unfixable_kinks:
          _report_unfixable_kinks(path, sharp_kinks)
  
      return Path(*new_path)
  