def test_rational_derivative(self): # testing the parametrization x(u,v) = [.5*u^3*(1-v)^3 / ((1-v)^3*(1-u)^3 + .5*u^3*(1-v)^3), 0] # dx/du = (6*u^2*(u - 1)^2)/(u^3 - 6*u^2 + 6*u - 2)^2 # d2x/du2 = -(12*u*(u^5 - 3*u^4 + 2*u^3 + 4*u^2 - 6*u + 2))/(u^3 - 6*u^2 + 6*u - 2)^3 # d3x/du3 = (12*(3*u^8 - 12*u^7 + 10*u^6 + 48*u^5 - 156*u^4 + 176*u^3 - 72*u^2 + 4))/(u^3 - 6*u^2 + 6*u - 2)^4 # dx/dv = 0 controlpoints = [[0, 0, 1], [0, 0, 0], [0, 0, 0], [.5, 0, .5], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]] basis = BSplineBasis(4) surf = Surface(basis, basis, controlpoints, rational=True) def expect_derivative(u, v): return (6 * u**2 * (u - 1)**2) / (u**3 - 6 * u**2 + 6 * u - 2)**2 def expect_derivative_2(u, v): return -(12 * u * (u**5 - 3 * u**4 + 2 * u**3 + 4 * u**2 - 6 * u + 2)) / ( u**3 - 6 * u**2 + 6 * u - 2)**3 def expect_derivative_3(u, v): return (12 * (3 * u**8 - 12 * u**7 + 10 * u**6 + 48 * u**5 - 156 * u**4 + 176 * u**3 - 72 * u**2 + 4)) / ( u**3 - 6 * u**2 + 6 * u - 2)**4 # insert a few more knots to spice things up surf.insert_knot([.3, .51], 0) surf.insert_knot([.41, .53, .92], 1) # test first derivatives self.assertAlmostEqual( surf.derivative(0.32, 0.22, d=(1, 0))[0], expect_derivative(0.32, 0.22)) self.assertAlmostEqual(surf.derivative(0.32, 0.22, d=(1, 0))[1], 0) self.assertAlmostEqual( surf.derivative(0.71, 0.22, d=(1, 0))[0], expect_derivative(0.71, 0.22)) self.assertAlmostEqual( surf.derivative(0.71, 0.62, d=(1, 0))[0], expect_derivative(0.71, 0.62)) # test second derivatives self.assertAlmostEqual( surf.derivative(0.32, 0.22, d=(2, 0))[0], expect_derivative_2(0.32, 0.22)) self.assertAlmostEqual( surf.derivative(0.71, 0.22, d=(2, 0))[0], expect_derivative_2(0.71, 0.22)) self.assertAlmostEqual( surf.derivative(0.71, 0.62, d=(2, 0))[0], expect_derivative_2(0.71, 0.62)) # all cross derivatives vanish in this particular example self.assertAlmostEqual(surf.derivative(0.32, 0.22, d=(1, 1))[0], 0) self.assertAlmostEqual(surf.derivative(0.32, 0.22, d=(2, 1))[0], 0) self.assertAlmostEqual(surf.derivative(0.32, 0.22, d=(1, 2))[0], 0) # test third derivatives self.assertAlmostEqual( surf.derivative(0.32, 0.22, d=(3, 0))[0], expect_derivative_3(0.32, 0.22)) self.assertAlmostEqual( surf.derivative(0.71, 0.22, d=(3, 0))[0], expect_derivative_3(0.71, 0.22)) self.assertAlmostEqual( surf.derivative(0.71, 0.62, d=(3, 0))[0], expect_derivative_3(0.71, 0.62)) # swapping u for v and symmetric logic surf.swap() self.assertAlmostEqual( surf.derivative(0.22, 0.32, d=(0, 2))[0], expect_derivative_2(0.32, 0.22)) self.assertAlmostEqual( surf.derivative(0.22, 0.71, d=(0, 2))[0], expect_derivative_2(0.71, 0.22)) self.assertAlmostEqual( surf.derivative(0.62, 0.71, d=(0, 2))[0], expect_derivative_2(0.71, 0.62)) self.assertAlmostEqual( surf.derivative(0.22, 0.32, d=(0, 3))[0], expect_derivative_3(0.32, 0.22)) self.assertAlmostEqual( surf.derivative(0.22, 0.71, d=(0, 3))[0], expect_derivative_3(0.71, 0.22)) self.assertAlmostEqual( surf.derivative(0.62, 0.71, d=(0, 3))[0], expect_derivative_3(0.71, 0.62))
def test_derivative(self): # knot vector [t_1, t_2, ... t_{n+p+1}] # polynomial degree p (order-1) # n basis functions N_i(t), for i=1...n # the power basis {1,t,t^2,t^3,...} can be expressed as: # 1 = sum N_i(t) # t = sum ts_i * N_i(t) # t^2 = sum t2s_i * N_i(t) # ts_i = sum_{j=i+1}^{i+p} t_j / p # t2s_i = sum_{j=i+1}^{i+p-1} sum_{k=j+1}^{i+p} t_j*t_k / (p 2) # (p 2) = binomial coefficient # creating the mapping: # x(u,v) = u^2*v + u(1-v) # y(u,v) = v controlpoints = [[0, 0], [1.0 / 4, 0], [3.0 / 4, 0], [.75, 0], [0, 1], [0, 1], [.5, 1], [1, 1]] basis1 = BSplineBasis(3, [0, 0, 0, .5, 1, 1, 1]) basis2 = BSplineBasis(2, [0, 0, 1, 1]) surf = Surface(basis1, basis2, controlpoints) # call evaluation at a 5x4 grid of points val = surf.derivative([0, .2, .5, .6, 1], [0, .2, .4, 1], d=(1, 0)) self.assertEqual(len(val.shape), 3) # result should be wrapped in 3-index tensor self.assertEqual(val.shape[0], 5) # 5 evaluation points in u-direction self.assertEqual(val.shape[1], 4) # 4 evaluation points in v-direction self.assertEqual(val.shape[2], 2) # 2 coordinates (x,y) self.assertAlmostEqual(surf.derivative(.2, .2, d=(1, 0))[0], .88) # dx/du=2uv+(1-v) self.assertAlmostEqual(surf.derivative(.2, .2, d=(1, 0))[1], 0) # dy/du=0 self.assertAlmostEqual(surf.derivative(.2, .2, d=(0, 1))[0], -.16) # dx/dv=u^2-u self.assertAlmostEqual(surf.derivative(.2, .2, d=(0, 1))[1], 1) # dy/dv=1 self.assertAlmostEqual(surf.derivative(.2, .2, d=(1, 1))[0], -.60) # d2x/dudv=2u-1 self.assertAlmostEqual(surf.derivative(.2, .2, d=(2, 0))[0], 0.40) # d2x/dudu=2v self.assertAlmostEqual(surf.derivative(.2, .2, d=(3, 0))[0], 0.00) # d3x/du3=0 self.assertAlmostEqual(surf.derivative(.2, .2, d=(0, 2))[0], 0.00) # d2y/dv2=0 # test errors and exceptions with self.assertRaises(ValueError): val = surf.derivative(-10, .5) # evalaute outside parametric domain with self.assertRaises(ValueError): val = surf.derivative(+10, .3) # evalaute outside parametric domain with self.assertRaises(ValueError): val = surf.derivative(.5, -10) # evalaute outside parametric domain with self.assertRaises(ValueError): val = surf.derivative(.5, +10) # evalaute outside parametric domain