On Thu, Jan 14, 2010 at 4:38 AM, Mark Bakker <mark...@gmail.com> wrote:
> Does matplotlib have a routine that can fit a cubic Bezier curve through an
> array of 2D points?
>
> I saw some Bezier routines in Path, but couldn't find what I am looking for.
>

As far as I know, no.

> If matplotlib doesn't have it, does anybody have another suggestion in
> Python?
>

The attached is what I have used once.
It uses scipy.interpolate.splprep to fit the input as a B-spline then
convert it to cubic bezier curve.
But I have no idea if this is the best way to do it.

The original code is from

http://mail.scipy.org/pipermail/scipy-dev/2007-February/006651.html

And I only added some mpl-related things.

Regards,

-JJ
import numpy as np
from scipy.interpolate import splprep

def b_spline_to_bezier_series(tck, per = False):
  """Convert a parametric b-spline into a sequence of Bezier curves of the same degree.
  
  Inputs:
    tck : (t,c,k) tuple of b-spline knots, coefficients, and degree returned by splprep.
    per : if tck was created as a periodic spline, per *must* be true, else per *must* be false.
  
  Output:
    A list of Bezier curves of degree k that is equivalent to the input spline.
    Each Bezier curve is an array of shape (k+1,d) where d is the dimension of the
    space; thus the curve includes the starting point, the k-1 internal control
    points, and the endpoint, where each point is of d dimensions.


    from http://mail.scipy.org/pipermail/scipy-dev/2007-February/006651.html

  """
  from scipy.interpolate.fitpack import insert
  from numpy import asarray, unique, split, sum, transpose
  t,c,k = tck
  t = asarray(t)
  try:
    c[0][0]
  except:
    # I can't figure out a simple way to convert nonparametric splines to 
    # parametric splines. Oh well.
    raise TypeError("Only parametric b-splines are supported.")
  new_tck = tck
  if per:
    # ignore the leading and trailing k knots that exist to enforce periodicity
    knots_to_consider = unique(t[k:-k])
  else:
    # the first and last k+1 knots are identical in the non-periodic case, so
    # no need to consider them when increasing the knot multiplicities below
    knots_to_consider = unique(t[k+1:-k-1])
  # For each unique knot, bring it's multiplicity up to the next multiple of k+1
  # This removes all continuity constraints between each of the original knots,
  # creating a set of independent Bezier curves.
  desired_multiplicity = k+1
  for x in knots_to_consider:
    current_multiplicity = sum(t == x)
    remainder = current_multiplicity%desired_multiplicity
    if remainder != 0:
      # add enough knots to bring the current multiplicity up to the desired multiplicity
      number_to_insert = desired_multiplicity - remainder
      new_tck = insert(x, new_tck, number_to_insert, per)
  tt,cc,kk = new_tck
  # strip off the last k+1 knots, as they are redundant after knot insertion
  bezier_points = transpose(cc)[:-desired_multiplicity]
  if per:
    # again, ignore the leading and trailing k knots
    bezier_points = bezier_points[k:-k]
  # group the points into the desired bezier curves
  return split(bezier_points, len(bezier_points) / desired_multiplicity, axis = 0)

def get_bezier_path(x, y, per=False):
  tck, uout = splprep([x, y], s=0., k=3, per=per)
  spl = b_spline_to_bezier_series(tck, per=per)

  # to mpl path
  from matplotlib.path import Path
  xy = [spl1[1:] for spl1 in spl]
  pp = np.concatenate([spl[0][:1]] + xy)
  codes = [Path.MOVETO] + [Path.CURVE4] * (3*len(xy))

  p = Path(pp, codes)
  return p

if __name__ == '__main__':
  #from mpl_toolkits.axes_grid.axis_artist import BezierPath
  from matplotlib.patches import PathPatch

  theta = np.linspace(0, 2*np.pi, 13)
  x = np.cos(theta)
  y = np.sin(theta)


  def myplot(ax, x, y, per=False):
    l1, = ax.plot(x, y, "rs", ms=10)
    p = get_bezier_path(x, y, per=per)
    l2, = ax.plot(p.vertices[:,0], p.vertices[:,1], "bo")
    bp = PathPatch(p, ec="b", fc="none")
    ax.add_patch(bp)

    ax.legend([l1, l2], ["input", "control points"])

  import matplotlib.pyplot as plt
  plt.clf()
  ax = plt.subplot(121)
  myplot(ax, theta, y)
  ax = plt.subplot(122)
  myplot(ax, x, y, per=True)
  plt.show()
  
  
------------------------------------------------------------------------------
Throughout its 18-year history, RSA Conference consistently attracts the
world's best and brightest in the field, creating opportunities for Conference
attendees to learn about information security's most important issues through
interactions with peers, luminaries and emerging and established companies.
http://p.sf.net/sfu/rsaconf-dev2dev
_______________________________________________
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-users

Reply via email to