加快最近点上的双曲抛物面算法抛物面、点上、算法、最近

2023-09-07 23:03:51 作者:兔子尾巴掉了

我写了发现表面的最近点的UV COORDS从查询点(P)的Python脚本。表面被逆时针列出从四个已知点制成四个直线边(P0,P1,P2,P3)中所定义

(请忽略小红球)

与我的方法的问题是,这是很慢(〜10秒至做5000查询具有低precision阈

我在寻找一个更好的方法来实现我想要什么,或建议,使我的code更有效。我唯一​​的限制是它必须用Python编写。

 进口numpy的为NP

#定义常量
LARGE_VALUE = 99999999.0
SMALL_VALUE = 0.00000001
子样本= 10.0

高清closestPointOnLineSegment(A,B,C):
    '''给定两点(A,B)界定的线段和一个查询(c)点
        返回的最近点在该段上,之间的距离
        从结果衍生查询和最近点,和Au值
    '''

    #检查是否c是相同a或b
    AC = C-A
    AC = np.linalg.norm(交流)
    如果AC == 0:
        返回C,0,0。

    BC = C-B
    BC = np.linalg.norm(BC)
    如果BC == 0:
        返回C,0,1。


    #看看段长度为0
    AB = B-一个
    AB = np.linalg.norm(AB)
    如果AB == 0:
        返回,0,0。

    #正常化段并做边测试
    AB = AB / AB
    测试= np.dot(AC,AB)
    如果测试< 0:
        返回,AC,0。
    ELIF试验> AB:
        回报B,BC,1。

    #返回上段,距离和U值最接近的xyz
    P =(测试* AB)+一
    回磷,np.linalg.norm(C-P),(测试/ AB)




高清surfaceWalk(E0,E1,P,V 0 = 0,V 1 = 1):
    '''走沿着2条边的表面上,对于每个样品段
        寻找最近点,递归直到都采样边缘
        比SMALL_VALUE小
    '''

    EDGE0 =(E1 [0] -e0 [0])
    边沿1 =(E1 [1] -e0 [1])
    LEN0 = np.linalg.norm(EDGE0 *(V1-V0))
    LEN1 = np.linalg.norm(EDGE1 *(V1-V0))

    VMIN = V0
    VMAX = V1
    closest_d = 0。
    closest_u = 0。
    closest_v = 0。
    二= 0。
    DIST = LARGE_VALUE

    因为我在范围内(INT(子样本)+1):
        V = V0 +((V1-V0)*(I /子样本))
        一个=(EDGE0 * v)的+ E0 [0]
        B =(边沿1 * V)+ E 0 [1]

        closest_p,closest_d,closest_u = closestPointOnLineSegment(A,B,p)的

        如果closest_d<距离:
            DIST = closest_d
            closest_v = V
            II = I

    #如果两个边长< = SMALL_VALUE,我们是我们的precision treshold内,以便返回结果
    如果LEN0< = SMALL_VALUE和LEN1< = SMALL_VALUE:
        返回closest_p,closest_d,closest_u,closest_v

    #阈值没有得到满足,设置V0 ANF V1限制closest_v的两侧,并保持递归
    VMIN = V0 +((V1-V0)*(np.clip((II-1),0。,子样品)/子样本))
    VMAX = V0 +((V1-V0)*(np.clip((ⅱ+ 1),0。,子样品)/子样本))
    返回surfaceWalk(E0,E1,P,VMIN,VMAX)




高清closestPointToPlane(P0,P1,P2,P3,P,调试=真):
    '''鉴于四点定义一个四面(P0,P1,p2,3)和
        查询点p。寻找最近的边缘,然后开始步行
        对面一端到下一个,直到我们找到的最接近点
    '''

    #寻找最近的优势,我们将使用这种优势来开始我们的散步
    C,D,U,V = surfaceWalk([P0,P1],[P3,P2],p)的
    如果调试:
        打印最近点:%s的%C
        打印距离点:%s的%D
        打印'U坐标:%s的%U
        打印'V坐标:%s的%V

    回报C,D,U,V



P0 = np.array([1.15,0.62,-1.01])
P1 = np.array(1.74,0.86,-0.8​​8])
P2 = np.array(1.79,0.40,-1.46])
P3 = np.array([0.91,0.79,-1.84])
P = np.array([1.17,0.94,-1.52])
closestPointToPlane(P0,P1,P2,P3,p)的


最近点:1.11588876 0.70474519 -1.52660706]
距离点:0.241488104197
ü坐标:0.164463481066
V坐标:0.681959858995
 

解决方案 简单漂亮的旋转图形有哪些,求图片

如果你的面,因为它似乎,双曲抛物面,您可以参数化了点取值上它为:

  S = P0 + U *(P1  -  P0)+ V *(P3  -  P0)+ U * V *(P2  -  P3  -  P1 + P0)
 

做事这样一来,该行 P0P3 有方程 U = 0 P1P2 U = 1 p0p1 V = 0 P2P3 V = 1 。我一直没能找出未来与分析前pression的最近点一个点 P 的一种方式,但 scipy.optimize 可以做的工作用数字为您提供:

 进口numpy的为NP
从scipy.optimize进口减少

P0 = np.array([1.15,0.62,-1.01])
P1 = np.array(1.74,0.86,-0.8​​8])
P2 = np.array(1.79,0.40,-1.46])
P3 = np.array([0.91,0.79,-1.84])
P = np.array([1.17,0.94,-1.52])

高清乐趣(X,P0,P1,P2,P3,p)的:
    U,V = X
    S = U *(P1-P0)+ V *(P3-P0)+ U * V *(P2-P3-P1 + P0)+ P0
    返回np.linalg.norm(P  -  S)

>>>最小化(乐趣,(0.5,0.5),(P0,P1,P2,P3,P))
  状态:0
 成功:真
    njev:9
    nfev:36
     好玩:0.24148810420527048
       X:阵列([0.16446403,0.68196253])
 消息:优化成功终止。
    赫斯:阵列([0.38032445,0.15919791]
       [0.15919791,0.44908365]])
     江淮汽车:阵列([-1.27032399e-06,6.74091280e-06])
 

的返回最小是一个对象,你可以通过它访问属性的值:

 >>>解析度=最小(乐趣,(0.5,0.5),(P0,P1,P2,P3,P))
>>> res.x#u和最近点诉坐标
阵列([0.16446403,0.68196253])
>>> res.fun#最小距离
0.24148810420527048
 

如何去寻找一个解决方案,而SciPy的...向量结合点有些指针取值参数化如上一个普通的点 P PS 。找出你可以走两头,让相同的结果不同的方法的最近点:

在计算该向量的长度,(PS)** 2 ,取及其衍生物WRT U v ,并将其等同于零。 在计算两个向量相切hypar在取值,它可以发现 DS /杜 DS / DV ,并处他们与 PS 为零。

如果你的工作这些了,你最终会得到两个方程,将需要大量的操作,以在类似的或者 U 或 v ,所以目前还没有确切的分析解决方案,尽管你可以解决这个数值只有numpy的。一种更容易的选择是制定出这些方程式,直到你得到这两个公式,其中 A = P1-P0 B = P3-P0 C = P2-P3-P1 + P0 S_ = S-P0 P_ = P-P0

  U =(P_  -  V * B)*(A + V * C)/(A + V * C)** 2
V =(P_  -  U * A)×(B + U * C)/(B + U * C)** 2
 

您不能想出一个这样的解析解很容易,但你可以希望,如果你用这两个关系来循环试验解决方案,它就会收敛。对于你的测试用例它的工作:

高清solve_uv(X0,P0,P1,P2,P3,P,TOL = 1E-6,硝石= 100):
    A = P1  -  P0
    B = P3  -  P0
    C = P2  -  P3  -  P1 + P0
    P_ = P  -  P0
    U,V = X0
    错误=无
    而硝石和(误差为无或错误> TOL):
        硝石 -  = 1
        U_ = np.dot(P_  -  V * B,A + V * C)/ np.dot(A + V * C,A + V * C)
        V_ = np.dot(P_  -  U * A,B + U * C)/ np.dot(B + U * C,B + U * C)
        错误= np.linalg.norm([U  -  U_,V  -  V_])
        U,V = U_,V_
    返回np.array([U,V]),np.linalg.norm(U * A + V * B + U * V * C + P0  -  P)

>>> solve_uv([0.5,0.5],P0,P1,P2,P3,p)的
(阵列([0.16446338,0.68195998]),0.2414881041967159)
 

我不认为这是保证收敛,但对于这种特殊情况下,它似乎工作pretty的罚款,并只需要15次迭代来得到所要求的能力。

I wrote a python script which finds the UV coords of the closest point on surface from a query point (p). The surface is defined by four linear edges made from four known points (p0,p1,p2,p3) listed counter clockwise.

(Please ignore the little red ball)

The problem with my approach is that it is very slow (~10 seconds to do 5000 queries with a low precision threshold.

I'm looking for a better approach to achieve what i want, or suggestions to make my code more efficient. My only constraint is that it must be written in python.

import numpy as np

# Define constants
LARGE_VALUE=99999999.0
SMALL_VALUE=0.00000001
SUBSAMPLES=10.0

def closestPointOnLineSegment(a,b,c):
    ''' Given two points (a,b) defining a line segment and a query point (c)
        return the closest point on that segment, the distance between
        query and closest points, and a u value derived from the results
    '''

    # Check if c is same as a or b
    ac=c-a
    AC=np.linalg.norm(ac)
    if AC==0.:
        return c,0.,0.

    bc=c-b
    BC=np.linalg.norm(bc)
    if BC==0.:
        return c,0.,1.


    # See if segment length is 0
    ab=b-a
    AB=np.linalg.norm(ab)
    if AB == 0.:
        return a,0.,0.

    # Normalize segment and do edge tests
    ab=ab/AB
    test=np.dot(ac,ab)
    if test < 0.:
        return a,AC,0.
    elif test > AB:
        return b,BC,1.

    # Return closest xyz on segment, distance, and u value
    p=(test*ab)+a
    return p,np.linalg.norm(c-p),(test/AB)




def surfaceWalk(e0,e1,p,v0=0.,v1=1.):
    ''' Walk on the surface along 2 edges, for each sample segment
        look for closest point, recurse until the both sampled edges
        are smaller than SMALL_VALUE
    '''

    edge0=(e1[0]-e0[0])
    edge1=(e1[1]-e0[1])
    len0=np.linalg.norm(edge0*(v1-v0))
    len1=np.linalg.norm(edge1*(v1-v0))

    vMin=v0
    vMax=v1
    closest_d=0.
    closest_u=0.
    closest_v=0.
    ii=0.
    dist=LARGE_VALUE

    for i in range(int(SUBSAMPLES)+1):
        v=v0+((v1-v0)*(i/SUBSAMPLES))
        a=(edge0*v)+e0[0]
        b=(edge1*v)+e0[1]

        closest_p,closest_d,closest_u=closestPointOnLineSegment(a,b,p)

        if closest_d < dist:
            dist=closest_d
            closest_v=v
            ii=i

    # If both edge lengths <= SMALL_VALUE, we're within our precision treshold so return results
    if len0 <= SMALL_VALUE and len1 <= SMALL_VALUE:
        return closest_p,closest_d,closest_u,closest_v

    # Threshold hasn't been met, set v0 anf v1 limits to either side of closest_v and keep recursing
    vMin=v0+((v1-v0)*(np.clip((ii-1),0.,SUBSAMPLES)/SUBSAMPLES))
    vMax=v0+((v1-v0)*(np.clip((ii+1),0.,SUBSAMPLES)/SUBSAMPLES))
    return surfaceWalk(e0,e1,p,vMin,vMax)




def closestPointToPlane(p0,p1,p2,p3,p,debug=True):
    ''' Given four points defining a quad surface (p0,p1,p2,3) and
        a query point p. Find the closest edge and begin walking
        across one end to the next until we find the closest point 
    '''

    # Find the closest edge, we'll use that edge to start our walk
    c,d,u,v=surfaceWalk([p0,p1],[p3,p2],p)
    if debug:
        print 'Closest Point:     %s'%c
        print 'Distance to Point: %s'%d
        print 'U Coord:           %s'%u
        print 'V Coord:           %s'%v

    return c,d,u,v



p0 = np.array([1.15, 0.62, -1.01])
p1 = np.array([1.74, 0.86, -0.88])
p2 = np.array([1.79, 0.40, -1.46])
p3 = np.array([0.91, 0.79, -1.84])
p =  np.array([1.17, 0.94, -1.52])
closestPointToPlane(p0,p1,p2,p3,p)


Closest Point:     [ 1.11588876  0.70474519 -1.52660706]
Distance to Point: 0.241488104197
U Coord:           0.164463481066
V Coord:           0.681959858995

解决方案

If your surface is, as it seems, a hyperbolic paraboloid, you can parametrize a point s on it as:

s = p0 + u * (p1 - p0) + v * (p3 - p0) + u * v * (p2 - p3 - p1 + p0)

Doing things this way, the line p0p3 has equation u = 0, p1p2 is u = 1, p0p1 is v = 0 and p2p3 is v = 1. I haven't been able to figure out a way of coming up with an analytical expression for the closest point to a point p, but scipy.optimize can do the job numerically for you:

import numpy as np
from scipy.optimize import minimize

p0 = np.array([1.15, 0.62, -1.01])
p1 = np.array([1.74, 0.86, -0.88])
p2 = np.array([1.79, 0.40, -1.46])
p3 = np.array([0.91, 0.79, -1.84])
p =  np.array([1.17, 0.94, -1.52])

def fun(x, p0, p1, p2, p3, p):
    u, v = x
    s = u*(p1-p0) + v*(p3-p0) + u*v*(p2-p3-p1+p0) + p0
    return np.linalg.norm(p - s)

>>> minimize(fun, (0.5, 0.5), (p0, p1, p2, p3, p))
  status: 0
 success: True
    njev: 9
    nfev: 36
     fun: 0.24148810420527048
       x: array([ 0.16446403,  0.68196253])
 message: 'Optimization terminated successfully.'
    hess: array([[ 0.38032445,  0.15919791],
       [ 0.15919791,  0.44908365]])
     jac: array([ -1.27032399e-06,   6.74091280e-06])

The return of minimize is an object, you can access the values through its attributes:

>>> res = minimize(fun, (0.5, 0.5), (p0, p1, p2, p3, p))
>>> res.x # u and v coordinates of the nearest point
array([ 0.16446403,  0.68196253])
>>> res.fun # minimum distance
0.24148810420527048

Some pointers on how to go about finding a solution without scipy... The vector joining the point s parametrized as above with a generic point p is p-s. TO find out the closest point you can go two different ways that give the same result:

Compute the length of that vector, (p-s)**2, take its derivatives w.r.t. u and v and equate them to zero. Compute two vectors tangent to the hypar at s, which can be found as ds/du and ds/dv, and impose that their inner product with p-s be zero.

If you work these out, you'll end up with two equations that would require a lot of manipulation to arrive at something like a third or fourth degree equation for either u or v, so there is no exact analytical solution, although you could solve that numerically with numpy only. An easier option is to work out those equations until you get these two equations, where a = p1-p0, b = p3-p0, c = p2-p3-p1+p0, s_ = s-p0, p_ = p-p0:

u = (p_ - v*b)*(a + v*c) / (a + v*c)**2
v = (p_ - u*a)*(b + u*c) / (b + u*c)**2

You can't come up with an analytical solution for this easily, but you can hope that if you use those two relations to iterate a trial solution, it will converge. For your test case it does work:

def solve_uv(x0, p0, p1, p2, p3, p, tol=1e-6, niter=100):
    a = p1 - p0
    b = p3 - p0
    c = p2 - p3 - p1 + p0
    p_ = p - p0
    u, v = x0
    error = None
    while niter and (error is None or error > tol):
        niter -= 1
        u_ = np.dot(p_ - v*b, a + v*c) / np.dot(a + v*c, a + v*c)
        v_ = np.dot(p_ - u*a, b + u*c) / np.dot(b + u*c, b + u*c)
        error = np.linalg.norm([u - u_, v - v_])
        u, v = u_, v_
    return np.array([u, v]), np.linalg.norm(u*a + v*b +u*v*c + p0 - p)

>>> solve_uv([0.5, 0.5], p0, p1, p2, p3, p)
(array([ 0.16446338,  0.68195998]), 0.2414881041967159)

I don't think this is guaranteed to converge, but for this particular case it seems to work pretty fine, and only needs 15 iterations to get to the requested tolerance.