简体   繁体   中英

Line and curve intersection in python using bezier curve and vertical line

I have a Bezier curve and am trying to intersect it with a vertical line. I don't have a background in maths so I was hoping to find the point of intersection using an intersection function without having to solve line equations. Any help would be greatly appreciated!

Here is what I have so far

#!/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()

这是图

Just use cubic root finding anyway. You don't need a background in math, all you need is to have someone that already did this for you, which there are. You want a python implementation of cubic root finding, but what you really want is any implmentation of cubic root finding for Bezier curves that can be adapted to Python, so I'm going to give you a JavaScript implementation and it should be almost trivial to adapt that to python.

First, we rotate the Bezier curve and line both such that the line becomes the x-axis. We do this, because we can then treat "intersection finding" as simply "root finding" (let's do this in pseudocode):

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)

done.

When expressed in terms of interpolation parameter (the Bezier curve control variable t in this case), intersections are invariant to linear transforms, so that's handy.

We then use Cardano's algorithm to do cubic root finding, which I explain over on http://pomax.github.io/bezierinfo/#extremities , and the code for which is based on 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);
  }
}

You compute the roots for your x and y functions separately, which will give you two lists of t values for which there might be intersections, and any t value that appears in both x and y lists is an intersection.

This code relies on two helper functions, which are pretty trivial but in the code I copied are of the form:

// 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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM