使用贝塞尔曲线和垂直线在python中的线和曲线交叉

时间:2016-02-26 11:49:16

标签: python curve bezier

我有一条Bezier曲线,我试图将它与垂直线相交。我没有数学背景所以我希望使用交叉函数找到交点,而不必求解线方程。任何帮助将不胜感激!

这是我到目前为止所拥有的

#!/bin/python

import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.lines import Line2D
import matplotlib.patches as patches
import matplotlib.ticker as plticker
import numpy as np

from matplotlib import cbook

from shapely.geometry import LineString, Point

verts = [
    (0., 0.),  # P0
    (0, 100), # P1
    (100, 0), # P2
    (100, 100), # P3
    ]

codes = [Path.MOVETO,
         Path.CURVE4,
         Path.CURVE4,
         Path.CURVE4,
         ]

path = Path(verts, codes)

fig = plt.figure()
ax = fig.add_subplot(111)
patch = patches.PathPatch(path, facecolor='none', lw=2, picker=0)

#add the line
ax.add_patch(patch)

xs, ys = zip(*verts)
#add the handles lines
#ax.plot(xs, ys, 'x--', lw=2, color='black', ms=10)

#ax.text(1, 1, 'P0')
#ax.text(20, 4, 'P1')
#ax.text(96, 80, 'P2')
#ax.text(96, 96, 'P3')

ax.set_xlim(-0.1, 100.1)
ax.set_ylim(-0.1, 100.1)

intervals = float(5)
loc = plticker.MultipleLocator(base=intervals)
ax.xaxis.set_major_locator(loc)
ax.yaxis.set_major_locator(loc)
ax.grid(True,'both')

#################

print ""
#print zip(*path.iter_segments(stroke_width=10.0,curves=False))

def on_move(event):
    #thisline = event.artist
    xmouse, ymouse = event.xdata, event.ydata
    #print "x: ", str(xmouse), ", y: ", str(ymouse)

def on_click(event):
    xmouse, ymouse = event.xdata, event.ydata
    print xmouse
    newline = Line2D([xmouse,xmouse], [0,100])
    ax.add_line(newline)
    fig.canvas.draw()

fig.canvas.mpl_connect('motion_notify_event', on_move)
fig.canvas.mpl_connect('button_press_event', on_click)

###############

plt.show()

here is the graph

1 个答案:

答案 0 :(得分:1)

无论如何都要使用立方根发现。你不需要数学背景,你只需要有一个已经为你做过这个的人。你想要一个python实现立方根发现,但你真正想要的是任何对Bezier曲线的立方根发现的实现,可以适应Python,所以我将给你一个JavaScript实现将它改编为python几乎是微不足道的。

首先,我们旋转Bezier曲线并将两者都排成一行,使得该线成为x轴。我们这样做,因为我们可以将“交叉点查找”视为简单的“根查找”(让我们用伪代码执行此操作):

pts = line.points + curve.points
offset = pts[0];
for all p in pts:
  p -= offset
angle = atan2(pts[1].y, pts[0].x)
for all p in pts:
  p = rotate(p, -angle)

进行。

当用插值参数(在本例中为贝塞尔曲线控制变量t)表示时,交点对线性变换是不变的,因此很方便。

然后我们使用Cardano算法进行立方根查找,我在http://pomax.github.io/bezierinfo/#extremities上解释,其代码基于http://www.trans4mind.com/personal_development/mathematics/polynomials/cubicAlgebra.htm

function getCubicRoots(pa, pb, pc, pd) {
  // pa...pd are our cubic bezier coordinates in one dimension
  var d = (-pa + 3*pb - 3*pc + pd),
      a = (3*pa - 6*pb + 3*pc) / d,
      b = (-3*pa + 3*pb) / d,
      c = pa / d;

  var p = (3*b - a*a)/3,
      p3 = p/3,
      q = (2*a*a*a - 9*a*b + 27*c)/27,
      q2 = q/2,
      discriminant = q2*q2 + p3*p3*p3;

  // and some variables we're going to use later on:
  var u1,v1,root1,root2,root3;

  // three possible real roots:
  if (discriminant < 0) {
    var mp3  = -p/3,
        mp33 = mp3*mp3*mp3,
        r    = sqrt( mp33 ),
        t    = -q / (2*r),
        cosphi = t<-1 ? -1 : t>1 ? 1 : t,
        phi  = acos(cosphi),
        crtr = cuberoot(r),
        t1   = 2*crtr;
    root1 = t1 * cos(phi/3) - a/3;
    root2 = t1 * cos((phi+2*pi)/3) - a/3;
    root3 = t1 * cos((phi+4*pi)/3) - a/3;
    return [root1, root2, root3].filter(accept);
  }

  // three real roots, but two of them are equal:
  else if(discriminant === 0) {
    u1 = q2 < 0 ? cuberoot(-q2) : -cuberoot(q2);
    root1 = 2*u1 - a/3;
    root2 = -u1 - a/3;
    return [root1, root2].filter(accept);
  }

  // one real root, two complex roots
  else {
    var sd = sqrt(discriminant);
    u1 = cuberoot(sd - q2);
    v1 = cuberoot(sd + q2);
    root1 = u1 - v1 - a/3;
    return [root1].filter(accept);
  }
}

您分别为xy函数计算根,这将为您提供两个可能为交叉点的t值列表, tx列表中显示的任何y值都是的交集。

这段代码依赖于两个辅助函数,这些函数非常简单,但在我复制的代码中有以下形式:

// A helper function to filter for values in the [0,1] interval:
function accept(t) {
  return 0<=t && t <=1;
}

// A real-cuberoots-only function:
function crt(v) {
  if(v<0) return -Math.pow(-v,1/3);
  return Math.pow(v,1/3);
}