4

누구든지이 시스템에 대한 해결책을 찾기 위해 수정 또는 대체 경로를 제안 할 수 있습니까? 특히 [0,1] x [0,1]의 솔루션 (s, t)에만 관심이 있습니다.큐빅 다항식의 시스템을 해결하십시오 (베 지어 곡선의 교차점을 찾으십시오)

참고 : 여기서 두 개의 3 차 베 지어 곡선의 교차점을 찾고 있습니다. 합리적인 시간 내에 모든 솔루션을 찾을 수있는 방법이 필요합니다. (내 용도로는 한 쌍의 곡선 당 몇 초를 의미합니다.)

sympy를 사용해 보았지만 solve() 및 solve_poly_system()이 모두 빈 목록을 반환했습니다.

from sympy.solvers import solve_poly_system, solve 
from sympy.abc import s,t 

#here are two cubics. I'm looking for their intersection in [0,1]x[0,1]: 
cub1 = 600*s**3 - 1037*s**2 + 274*s + 1237*t**3 - 2177*t**2 + 642*t + 77 
cub2 = -534*s**3 + 582*s**2 + 437*s + 740*t**3 - 1817*t**2 + 1414*t - 548 

#I know such a solution exists (from plotting these curves) and fsolve finds an  approximation of it no problem: 
from scipy.optimize import fsolve 
fcub1 = lambda (s,t): 600*s**3 - 1037*s**2 + 274*s + 1237*t**3 - 2177*t**2 + 642*t + 77 
fcub2 = lambda (s,t):-534*s**3 + 582*s**2 + 437*s + 740*t**3 - 1817*t**2 + 1414*t - 548 
F = lambda x: [fcub1(x),fcub2(x)] 
print 'fsolve gives (s,t) = ' + str(fsolve(F,(0.5,0.5))) 
print 'F(s,t) = ' + str(F(fsolve(F,(0.5,0.5)))) 

#solve returns an empty list 
print solve([cub1,cub2]) 

#solve_poly_system returns a DomainError: can't compute a Groebner basis over RR 
print solve_poly_system([cub1,cub2]) 

이 출력 : 읽기위한

fsolve gives (s,t) = [ 0.35114023 0.50444115] 
F(s,t) = [4.5474735088646412e-13, 0.0] 
[] 
[] 

감사

여기 내 코드입니다!

+0

나는 이것이 sympy로 해결할 수 없다고 생각합니다. 예외로는 euqations에 float 값을 사용하기 때문입니다.'solve_poly_system'의 소스 코드에는'a = 2와 b <= 2이고 c <= 2이고 d <= 2 : – HYRY

+0

인 경우를 확인하는 줄이 있습니다. @HYRY : 맞아요. DomainError. 그것을 확인하기 위해 모든 소수 자릿수를 제거하는 의미지만, 나는 하나를 놓쳤습니다! 또한 solve_poly_system은 각 다항식의 차수가 3보다 작은 시스템에서만 사용할 수 있다고 생각하십니까? – AndyP

답변

4

을 더 접근 방식이있다. (http://pomax.github.io/bezierinfo/#curveintersection, http://www.tsplines.com/technology/edu/CurveIntersection.pdf).

간단한 해결책에 대한 권장 사항 : 베 지어 세분 알고리즘 (http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/Bezier/bezier-sub.html)을 구현하십시오. 두 곡선 모두에 대해 제어 정점의 경계 상자를 계산하십시오. 중복되는 경우 교차가 가능하며 절반으로 세분화되고 반복됩니다 (이번에는 4 번의 비교가 있습니다). 재귀 적으로 계속하십시오.

오히려 많은 상자가 겹쳐지는 것을 방지하기 때문에 지수 폭발 (1, 4, 16, 256 ... 비교)을 두려워해서는 안됩니다.

이론 상으로는 제어점의 볼록 선체로 작업 할 수 있지만 실제로는 간단한 경계 상자로 작업하기가 쉽습니다.

+0

이것이 제 목적을 위해 잘 작동한다고 생각합니다. 나는 오늘 밤 그것을 구현하려고 노력할 것입니다. 감사. – AndyP

+0

대단히 고맙습니다! 나는 아래 코드를 붙여 넣었다. – AndyP

+0

당신을 기쁘게합니다. 베 지어 곡선에는 좋은 특성이 있습니다. –

0

시스템을 단순화하면 어떨까요?

적절한 선형 조합으로 s^3 또는 t^3 중 하나를 제거하고 나머지 2 차 방정식을 풀 수 있습니다. 다른 방정식에 결과를 연결하면 하나의 미지수에서 단일 방정식을 얻을 수 있습니다.

또는 합력에 의해 얻어진 대수 방정식 해결 : 베지의 교차로

36011610661302281 - 177140805507270756*s - 201454039857766711*s^2 + 1540826307929388607*s^3 + 257712262726095899*s^4 - 4599101672917940010*s^5 + 1114665205197856508*s^6 + 6093758014794453276*s^7 - 5443785088068396888*s^8 + 1347614193395309112*s^9 = 0 

(http://www.dr-mikes-maths.com/polynomial-reduction.html)

+0

나는이 방법에 익숙하지 않다고 생각한다. 그래서 당신은 이것이 임의의 두 입방체 (특히 베 지어) 커브의 교차점을 찾는 데 효과적 일 것이라고 생각합니까? – AndyP

+0

네,하지만 그게 베지에의 교차점인지는 몰랐습니다. 선체 속성을 이용하여이 문제에 대한보다 적절한 접근법이 있습니다. –

1

이브의 솔루션은 잘 작동했습니다. 여기 경우 내 코드의 IT 도움이 사람 :

또한 사람이 그것을 원하는 경우에 대비, 나는 임의 정도의 베 지어 곡선을 분할에 대한 Casteljau 알고리즘 드 코딩 쓴
from math import sqrt 
def cubicCurve(P,t): 
    return P[0]*(1-t)**3 + 3*P[1]*t*(1-t)**2 + 3*P[2]*(1-t)*t**2 + P[3]*t**3 
def cubicMinMax_x(points): 
    local_extremizers = [0,1] 
    a = [p.real for p in points] 
    delta = a[1]**2 - (a[0] + a[1]*a[2] + a[2]**2 + (a[0] - a[1])*a[3]) 
    if delta>=0: 
     sqdelta = sqrt(delta)/(a[0] - 3*a[1] + 3*a[2] - a[3]) 
     tau = a[0] - 2*a[1] + a[2] 
     r1 = tau+sqdelta 
     r2 = tau-sqdelta 
     if 0<r1<1: 
      local_extremizers.append(r1) 
     if 0<r2<1: 
      local_extremizers.append(r2) 
    localExtrema = [cubicCurve(a,t) for t in local_extremizers] 
    return min(localExtrema),max(localExtrema) 
def cubicMinMax_y(points): 
    return cubicMinMax_x([-1j*p for p in points]) 
def intervalIntersectionWidth(a,b,c,d): #returns width of the intersection of intervals [a,b] and [c,d] (thinking of these as intervals on the real number line) 
    return max(0, min(b, d) - max(a, c)) 
def cubicBoundingBoxesIntersect(cubs):#INPUT: 2-tuple of cubics (given bu control points) #OUTPUT: boolean 
    x1min,x1max = cubicMinMax_x(cubs[0]) 
    y1min,y1max = cubicMinMax_y(cubs[0]) 
    x2min,x2max = cubicMinMax_x(cubs[1]) 
    y2min,y2max = cubicMinMax_y(cubs[1]) 
    if intervalIntersectionWidth(x1min,x1max,x2min,x2max) and intervalIntersectionWidth(y1min,y1max,y2min,y2max): 
     return True 
    else: 
     return False 
def cubicBoundingBoxArea(cub_points):#INPUT: 2-tuple of cubics (given bu control points) #OUTPUT: boolean 
    xmin,xmax = cubicMinMax_x(cub_points) 
    ymin,ymax = cubicMinMax_y(cub_points) 
    return (xmax-xmin)*(ymax-ymin) 
def halveCubic(P): 
    return ([P[0], (P[0]+P[1])/2, (P[0]+2*P[1]+P[2])/4, (P[0]+3*P[1]+3*P[2]+P[3])/8],[(P[0]+3*P[1]+3*P[2]+P[3])/8,(P[1]+2*P[2]+P[3])/4,(P[2]+P[3])/2,P[3]]) 
class Pair(object): 
    def __init__(self,cub1,cub2,t1,t2): 
     self.cub1 = cub1 
     self.cub2 = cub2 
     self.t1 = t1 #the t value to get the mid point of this curve from cub1 
     self.t2 = t2 #the t value to get the mid point of this curve from cub2 
def cubicXcubicIntersections(cubs): 
#INPUT: a tuple cubs=([P0,P1,P2,P3], [Q0,Q1,Q2,Q3]) defining the two cubic to check for intersections between. See cubicCurve fcn for definition of P0,...,P3 
#OUTPUT: a list of tuples (t,s) in [0,1]x[0,1] such that cubicCurve(cubs[0],t) - cubicCurve(cubs[1],s) < Tol_deC 
#Note: This will return exactly one such tuple for each intersection (assuming Tol_deC is small enough) 
    Tol_deC = 1 ##### This should be set based on your accuracy needs. Making it smaller will have relatively little effect on performance. Mine is set to 1 because this is the area of a pixel in my setup and so the curve (drawn by hand/mouse) is only accurate up to a pixel at most. 
    maxIts = 100 ##### This should be something like maxIts = 1-log(Tol_deC/length)/log(2), where length is the length of the longer of the two cubics, but I'm not actually sure how close to being parameterized by arclength these curves are... so I guess I'll leave that as an exercise for the interested reader :) 
    pair_list = [Pair(cubs[0],cubs[1],0.5,0.5)] 
    intersection_list = [] 
    k=0 
    while pair_list != []: 
     newPairs = [] 
     delta = 0.5**(k+2) 
     for pair in pair_list: 
      if cubicBoundingBoxesIntersect((pair.cub1,pair.cub2)): 
       if cubicBoundingBoxArea(pair.cub1)<Tol_deC and cubicBoundingBoxArea(pair.cub2)<Tol_deC: 
        intersection_list.append((pair.t1,pair.t2)) #this is the point in the middle of the pair 
        for otherPair in pair_list: 
         if pair.cub1==otherPair.cub1 or pair.cub2==otherPair.cub2 or pair.cub1==otherPair.cub2 or pair.cub2==otherPair.cub1: 
          pair_list.remove(otherPair) #this is just an ad-hoc fix to keep it from repeating intersection points 
       else: 
        (c11,c12) = halveCubic(pair.cub1) 
        (t11,t12) = (pair.t1-delta,pair.t1+delta) 
        (c21,c22) = halveCubic(pair.cub2) 
        (t21,t22) = (pair.t2-delta,pair.t2+delta) 
        newPairs += [Pair(c11,c21,t11,t21), Pair(c11,c22,t11,t22), Pair(c12,c21,t12,t21), Pair(c12,c22,t12,t22)] 
     pair_list = newPairs 
     k += 1 
     if k > maxIts: 
      raise Exception ("cubicXcubicIntersections has reached maximum iterations without terminating... either there's a problem/bug or you can fix by raising the max iterations or lowering Tol_deC") 
    return intersection_list 

. 위의 코드에서 나는 단지 그것을 Casteljau의 방법이지만 halveCubic으로 바꾸었지만 명시 적으로 3 차 사례 (t = 0.5)로 제한했습니다.

def splitBezier(points,t): 
#returns 2 tuples of control points for the two resulting Bezier curves 
    points_left=[] 
    points_right=[] 
    (points_left,points_right) = splitBezier_deCasteljau_recursion((points_left,points_right),points,t) 
    points_right.reverse() 
    return (points_left,points_right) 
def splitBezier_deCasteljau_recursion(cub_lr,points,t): 
    (cub_left,cub_right)=cub_lr 
    if len(points)==1: 
     cub_left.append(points[0]) 
     cub_right.append(points[0]) 
    else: 
     n = len(points)-1 
     newPoints=[None]*n 
     cub_left.append(points[0]) 
     cub_right.append(points[n]) 
     for i in range(n): 
      newPoints[i] = (1-t)*points[i] + t*points[i+1] 
     (cub_left, cub_right) = splitBezier_deCasteljau_recursion((cub_left,cub_right),newPoints,t) 
    return (cub_left, cub_right) 

나는 그 사람을 돕기를 바랍니다. Yves에게 도움을 주셔서 다시 한 번 감사드립니다.