diff --git a/geomdl/Abstract.py b/geomdl/Abstract.py index d5d7e951..1c4b102e 100644 --- a/geomdl/Abstract.py +++ b/geomdl/Abstract.py @@ -1181,6 +1181,10 @@ def insert_knot(self, **kwargs): """ Abstract method for implementation of knot insertion algorithm. """ pass + @abc.abstractmethod + def derivatives_ctrlpts(self, **kwargs): + """ Abstract method for implementation of the control points derivative algorithm. """ + pass class SurfaceEvaluator(six.with_metaclass(abc.ABCMeta, object)): """ Surface customizations for the Evaluator abstract base class. """ @@ -1198,6 +1202,10 @@ def insert_knot_v(self, **kwargs): """ Abstract method for implementation of knot insertion algorithm on the v-direction. """ pass + @abc.abstractmethod + def derivatives_ctrlpts(self, **kwargs): + """ Abstract method for implementation of the control points derivative algorith. """ + pass class SurfaceTessellator(six.with_metaclass(abc.ABCMeta, object)): """ Tessellator abstract base for the surface evaluator classes. """ diff --git a/geomdl/BSpline.py b/geomdl/BSpline.py index 72d58a9e..cc920aed 100644 --- a/geomdl/BSpline.py +++ b/geomdl/BSpline.py @@ -265,6 +265,29 @@ def derivatives(self, u=-1, order=0): ctrlpts=self._control_points, dimension=self._dimension) + # Evaluates the curve derivative control points + def derivatives_ctrlpts(self, **kwargs): + """ Evaluates the n-th order curve derivatives control points. + + :param order: derivative order + :type order: integer + :return: a list containing the control points of up to {order}-th derivative of the curve + :rtype: list + """ + # Check all parameters are set before the curve evaluation + self._check_variables() + + r1 = kwargs.get('r1', 0) + r2 = kwargs.get('r2', len(self._control_points) - 1) + order = kwargs.get('order', 0) + + return self._evaluator.derivatives_ctrlpts(r1=r1, r2=r2, + deriv_order=order, + degree=self.degree, + knotvector=self.knotvector, + ctrlpts=self._control_points, + dimension=self._dimension) + # Evaluates the curve tangent at the given u parameter def tangent(self, u=-1, normalize=False): """ Evaluates the curve tangent vector at the given parameter value. @@ -1060,6 +1083,31 @@ def derivatives(self, u=-1, v=-1, order=0): # Return the derivatives return SKL + # Evaluates the curve derivative control points + def derivatives_ctrlpts(self, **kwargs): + """ Evaluates the n-th order surface derivatives control points. + + :param order: derivative order + :type order: integer + :return: a list PKL, where PKL[k][l][i][j] is the {i, j}-th control point of the derivative of the surface S(u, v) w.r.t u k times and v l times + :rtype: list + """ + + r1 = kwargs.get('r1', 0) + r2 = kwargs.get('r2', self.ctrlpts_size_u - 1) + s1 = kwargs.get('s1', 0) + s2 = kwargs.get('s2', self.ctrlpts_size_v - 1) + order = kwargs.get('order', 0) + + return self._evaluator.derivatives_ctrlpts(r1=r1, r2=r2, + s1=s1, s2=s2, + degree_u=self.degree_u, degree_v=self.degree_v, + knotvector_u=self.knotvector_u, knotvector_v=self.knotvector_v, + ctrlpts_size_u=self.ctrlpts_size_u, ctrlpts_size_v=self.ctrlpts_size_v, + ctrlpts=self._control_points2D, + dimension=self._dimension, + deriv_order=order) + # Evaluates the surface tangent vectors at the given (u, v) parameter def tangent(self, u=-1, v=-1, normalize=False): """ Evaluates the surface tangent vector at the given (u, v) parameter pair. diff --git a/geomdl/evaluators.py b/geomdl/evaluators.py index ff9022a9..a70f73fb 100644 --- a/geomdl/evaluators.py +++ b/geomdl/evaluators.py @@ -20,6 +20,7 @@ class CurveEvaluator(Abstract.Evaluator, Abstract.CurveEvaluator): * Algorithm A3.1: CurvePoint * Algorithm A3.2: CurveDerivsAlg1 + * Algorithm A3.3: CurveDerivCpts * Algorithm A5.1: CurveKnotIns """ @@ -117,6 +118,40 @@ def derivatives(self, **kwargs): # Not implemented, yet... raise NotImplementedError("This functionality is not implemented at the moment") + # Computes the control points of all derivative curves up to and including the {degree}-th derivative + def derivatives_ctrlpts(self, **kwargs): + """ Computes the control points of all derivative curves up to and including the {degree}-th derivative. + + Output is PK[k][i], i-th control point of the k-th derivative curve where 0 <= k <= degree and r1 <= i <= r2-k + """ + # Call parent method + super(CurveEvaluator, self).derivatives_ctrlpts(**kwargs) + + # r1 - minimum span, r2 - maximum span + r1 = kwargs.get('r1') + r2 = kwargs.get('r2') + deriv_order = kwargs.get('deriv_order') + degree = kwargs.get('degree') + knot_vector = kwargs.get('knotvector') + control_points = kwargs.get('ctrlpts') + dimension = kwargs.get('dimension') + + # Algorithm A3.3 + r = r2 - r1 + PK = [[[None for _ in range(dimension)] for _ in range(r + 1)] for _ in range(deriv_order + 1)] + for i in range(0, r + 1): + PK[0][i][:] = [elem for elem in control_points[r1 + i]] + + for k in range(1, deriv_order + 1): + tmp = degree - k + 1 + for i in range(0, r - k + 1): + PK[k][i][:] = [tmp * (elem1 - elem2) / ( + knot_vector[r1 + i + degree + 1] - knot_vector[r1 + i + k]) for elem1, elem2 + in zip(PK[k - 1][i + 1], PK[k - 1][i])] + + # Return a 2-dimensional list of control points + return PK + def insert_knot(self, **kwargs): """ Insert knot multiple times at a single parameter. """ # Call parent method @@ -184,7 +219,6 @@ class CurveEvaluator2(CurveEvaluator): This evaluator implements the following algorithms from **The NURBS Book**: * Algorithm A3.1: CurvePoint - * Algorithm A3.3: CurveDerivCpts * Algorithm A3.4: CurveDerivsAlg2 * Algorithm A5.1: CurveKnotIns @@ -193,43 +227,14 @@ class CurveEvaluator2(CurveEvaluator): def __init__(self, **kwargs): super(CurveEvaluator2, self).__init__(**kwargs) - # Computes the control points of all derivative curves up to and including the {degree}-th derivative - @staticmethod - def derivatives_ctrlpts(**kwargs): - """ Computes the control points of all derivative curves up to and including the {degree}-th derivative. - - Output is PK[k][i], i-th control point of the k-th derivative curve where 0 <= k <= degree and r1 <= i <= r2-k - """ - # r1 - minimum span, r2 - maximum span - r1 = kwargs.get('r1', 0) - r2 = kwargs.get('r2', 0) - deriv_order = kwargs.get('deriv_order', 0) - degree = kwargs.get('degree') - knot_vector = kwargs.get('knotvector') - control_points = kwargs.get('ctrlpts') - dimension = kwargs.get('dimension') - - # Algorithm A3.3 - r = r2 - r1 - PK = [[[None for _ in range(dimension)] for _ in range(r + 1)] for _ in range(deriv_order + 1)] - for i in range(0, r + 1): - PK[0][i][:] = [elem for elem in control_points[r1 + i]] - - for k in range(1, deriv_order + 1): - tmp = degree - k + 1 - for i in range(0, r - k + 1): - PK[k][i][:] = [tmp * (elem1 - elem2) / ( - knot_vector[r1 + i + degree + 1] - knot_vector[r1 + i + k]) for elem1, elem2 - in zip(PK[k - 1][i + 1], PK[k - 1][i])] - - # Return a 2-dimensional list of control points - return PK - # Evaluates the curve derivative using "CurveDerivsAlg2" algorithm def derivatives_single(self, **kwargs): """ Evaluates n-th order curve derivatives at a single parameter. """ + # Call parent method + super(CurveEvaluator2, self).derivatives_single(**kwargs) + knot = kwargs.get('knot') - deriv_order = kwargs.get('deriv_order') + deriv_order = kwargs.get('deriv_order', 0) degree = kwargs.get('degree') knot_vector = kwargs.get('knotvector') control_points = kwargs.get('ctrlpts') @@ -242,7 +247,12 @@ def derivatives_single(self, **kwargs): span = helpers.find_span(knot_vector, len(control_points), knot) bfuns = helpers.basis_function_all(degree, tuple(knot_vector), span, knot) - PK = self.derivatives_ctrlpts(order=du, r1=(span - degree), r2=span, **kwargs) + PK = self.derivatives_ctrlpts(r1=(span - degree), r2=span, + deriv_order=du, + degree=degree, + knotvector=knot_vector, + ctrlpts=control_points, + dimension=dimension) for k in range(0, du + 1): CK[k] = [0.0 for _ in range(dimension)] @@ -324,6 +334,7 @@ class SurfaceEvaluator(Abstract.Evaluator, Abstract.SurfaceEvaluator): * Algorithm A3.5: SurfacePoint * Algorithm A3.6: SurfaceDerivsAlg1 + * Algorithm A3.7: SurfaceDerivCpts * Algorithm A5.3: SurfaceKnotIns """ @@ -466,6 +477,76 @@ def derivatives(self, **kwargs): # Not implemented, yet raise NotImplementedError("This functionality is not implemented at the moment") + # Computes the control points of all derivative surfaces up to and including the {degree}-th derivative using "SurfacederivCpts" + def derivatives_ctrlpts(self, **kwargs): + """ Computes the control points of all derivative surfaces up to and including the {degree}-th derivative. + + Output is PKL[k][l][i][j], i,j-th control point of the surface differentiated k times with respect to u and l times with respecto to v. + """ + # Call parent method + super(SurfaceEvaluator, self).derivatives_ctrlpts(**kwargs) + + r1 = kwargs.get('r1') # minimum span on U + r2 = kwargs.get('r2') # maximum span on U + s1 = kwargs.get('s1') # minimum span on V + s2 = kwargs.get('s2') # maximum span on V + + ctrlpts_size_u = kwargs.get('ctrlpts_size_u') + degree_u = kwargs.get('degree_u') + knot_vector_u = kwargs.get('knotvector_u') + + ctrlpts_size_v = kwargs.get('ctrlpts_size_v') + degree_v = kwargs.get('degree_v') + knot_vector_v = kwargs.get('knotvector_v') + + control_points2d = kwargs.get('ctrlpts') + deriv_order = kwargs.get('deriv_order') + + dimension = kwargs.get('dimension') + + PKL = [[[[[None for _ in range(dimension)] + for _ in range(ctrlpts_size_v)] for _ in range(ctrlpts_size_u)] + for _ in range(deriv_order + 1)] for _ in range(deriv_order + 1)] + + du = min(degree_u, deriv_order) + dv = min(degree_v, deriv_order) + + r = r2 - r1 + s = s2 - s1 + + curve_evaluator = CurveEvaluator(); + + # Control points of the U derivatives of every U-curve + for j in range(s1, s2 + 1): + PKu = curve_evaluator.derivatives_ctrlpts(r1=r1, r2=r2, + deriv_order=du, degree=degree_u, + knotvector=knot_vector_u, + ctrlpts=[control_points2d[_][j] for _ in range(0, len(control_points2d))], + dimension=dimension) + + # Copy into output as the U partial derivatives + for k in range(0, du + 1): + for i in range(0, r - k + 1): + PKL[k][0][i][j - s1] = PKu[k][i] + + # Control points of the V derivatives of every U derivated V-curve + for k in range(0, du): + for i in range(0, r - k + 1): + dd = min(deriv_order - k, dv) + + PKuv = curve_evaluator.derivatives_ctrlpts(r1 = 0, r2 = s, + deriv_order=dd, degree=degree_v, + knotvector=knot_vector_v[s1:], + ctrlpts=PKL[k][0][i], + dimension=dimension) + + # Copy into output + for l in range(1, dd + 1): + for j in range(0, s - l + 1): + PKL[k][l][i][j] = PKuv[l][j] + + return PKL + def insert_knot_u(self, **kwargs): """ Inserts knot(s) in u-direction. """ # Call parent method @@ -593,6 +674,72 @@ def insert_knot_v(self, **kwargs): return VQ, Q +class SurfaceEvaluator2(SurfaceEvaluator): + """ Sequential B-Spline surface evaluation algorithms. + + This evaluator implements the following algorithms from **The NURBS Book**: + + * Algorithm A3.5: SurfacePoint + * Algorithm A3.8: SurfaceDerivsAlg2 + * Algorithm A5.3: SurfaceKnotIns + + """ + + def __init__(self, **kwargs): + super(SurfaceEvaluator2, self).__init__(**kwargs) + + # Evaluates the surface derivatives using "SurfaceDerivsAlg2" + def derivatives_single(self, **kwargs): + """ Evaluates the n-th order surface derivatives at (u, v) parameters. + + Output is SKL[k][l], derivative of the surface k times with respect to U and l times with respect to V + """ + + deriv_order = kwargs.get('deriv_order') + knot_u = kwargs.get('knot_u') + knot_v = kwargs.get('knot_v') + degree_u = kwargs.get('degree_u') + degree_v = kwargs.get('degree_v') + knot_vector_u = kwargs.get('knotvector_u') + knot_vector_v = kwargs.get('knotvector_v') + control_points2d = kwargs.get('ctrlpts') + ctrlpts_size_u = kwargs.get('ctrlpts_size_u') + ctrlpts_size_v = kwargs.get('ctrlpts_size_v') + dimension = kwargs.get('dimension') + + SKL = [[[0.0 for _ in range(dimension)] for _ in range(deriv_order + 1)] for _ in range(deriv_order + 1)] + + du = min(degree_u, deriv_order) + dv = min(degree_v, deriv_order) + + span_u = helpers.find_span(knot_vector_u, ctrlpts_size_u, knot_u) + bfuns_u = helpers.basis_function_all(degree_u, tuple(knot_vector_u), span_u, knot_u) + span_v = helpers.find_span(knot_vector_v, ctrlpts_size_v, knot_v) + bfuns_v = helpers.basis_function_all(degree_v, tuple(knot_vector_v), span_v, knot_v) + + PKL = self.derivatives_ctrlpts(r1 = span_u - degree_u, r2 = span_u, + s1 = span_v - degree_v, s2 = span_v, + **kwargs) + + # Evaluating the derivative at parameters (u, v) using its control points + for k in range(0, du + 1): + dd = min(deriv_order - k, dv) + + for l in range(0, dd + 1): + SKL[k][l] = [0.0 for _ in range(dimension)] + + for i in range(0, degree_v - l + 1): + temp = [0.0 for _ in range(dimension)] + + for j in range(0, degree_u - k + 1): + temp[:] = [elem + (bfuns_u[j][degree_u - k] * drv_ctl_p) for elem, drv_ctl_p in + zip(temp, PKL[k][l][j][i])] + + SKL[k][l][:] = [elem + (bfuns_v[i][degree_v - l] * drv_ctl_p) for elem, drv_ctl_p in + zip(SKL[k][l], temp)] + + return SKL + class NURBSSurfaceEvaluator(SurfaceEvaluator): """ Sequential NURBS surface evaluation algorithms. diff --git a/tests/test_BSpline_Curve2D.py b/tests/test_BSpline_Curve2D.py index 88a14003..db9ad5a0 100644 --- a/tests/test_BSpline_Curve2D.py +++ b/tests/test_BSpline_Curve2D.py @@ -177,6 +177,46 @@ def test_bspline_curve2d_eval5(): assert abs(evalpt[1] - res[1]) < GEOMDL_DELTA +def test_bspline_curve2d_deriv_ctrlpts(): + test_degree = 3 + test_knotvector = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] + test_u = 0.35 + test_order = test_degree + + # Create a curve instance + curve = OBJECT_INSTANCE() + + # Set curve degree + curve.degree = test_degree + + # Set control points + curve.ctrlpts = CONTROL_POINTS + + # Set knot vector + curve.knotvector = test_knotvector + + # Take the derivative + der1 = curve.derivatives(u=test_u, order=test_order) + + # Compute control points of the derivative + deriv_ctrlpts = curve.derivatives_ctrlpts(order=test_order - 1) + + for k in range(0, test_order): + curvek = OBJECT_INSTANCE() + curvek.degree = test_degree - k + + # Cutting out None values in deriv_ctrlpts[k] and excess clamping values in test_knotvector + if k == 0: + curvek.ctrlpts = deriv_ctrlpts[k] + curvek.knotvector = test_knotvector + else: + curvek.ctrlpts = deriv_ctrlpts[k][:-k] + curvek.knotvector = test_knotvector[k:-k] + + assert abs(curvek.curvept(test_u)[0] - der1[k][0]) < GEOMDL_DELTA + assert abs(curvek.curvept(test_u)[1] - der1[k][1]) < GEOMDL_DELTA + + def test_bspline_curve2d_deriv1(): # Create a curve instance curve = OBJECT_INSTANCE() @@ -193,6 +233,7 @@ def test_bspline_curve2d_deriv1(): # Take the derivative der1 = curve.derivatives(u=0.35, order=2) curve.evaluator = evaluators.CurveEvaluator2() + der2 = curve.derivatives(u=0.35, order=2) assert abs(der1[0][0] - der2[0][0]) < GEOMDL_DELTA diff --git a/tests/test_BSpline_Curve3D.py b/tests/test_BSpline_Curve3D.py index 0125fe10..904b7e5a 100644 --- a/tests/test_BSpline_Curve3D.py +++ b/tests/test_BSpline_Curve3D.py @@ -161,6 +161,47 @@ def test_bspline_curve3d_bbox(): assert abs(to_check[1][2] - result[1][2]) < GEOMDL_DELTA +def test_bspline_curve3d_deriv_ctrlpts(): + test_degree = 4 + test_knotvector = [0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.3, 0.5, 0.7, 0.9, 1.0, 1.0, 1.0, 1.0, 1.0] + test_u = 0.35 + test_order = 3 + + # Create a curve instance + curve = OBJECT_INSTANCE() + + # Set curve degree + curve.degree = test_degree + + # Set control points + curve.ctrlpts = CONTROL_POINTS + + # Set knot vector + curve.knotvector = test_knotvector + + # Take the derivative + der1 = curve.derivatives(u=test_u, order=test_order) + + # Compute control points of the derivative + deriv_ctrlpts = curve.derivatives_ctrlpts(order=test_order - 1) + + for k in range(0, test_order): + curvek = OBJECT_INSTANCE() + curvek.degree = test_degree - k + + # Cutting out None values in deriv_ctrlpts[k] and excess clamping values in test_knotvector + if k == 0: + curvek.ctrlpts = deriv_ctrlpts[k] + curvek.knotvector = test_knotvector + else: + curvek.ctrlpts = deriv_ctrlpts[k][:-k] + curvek.knotvector = test_knotvector[k:-k] + + assert abs(curvek.curvept(test_u)[0] - der1[k][0]) < GEOMDL_DELTA + assert abs(curvek.curvept(test_u)[1] - der1[k][1]) < GEOMDL_DELTA + assert abs(curvek.curvept(test_u)[2] - der1[k][2]) < GEOMDL_DELTA + + def test_bspline_curve3d_deriv1(): # Create a curve instance curve = OBJECT_INSTANCE() diff --git a/tests/test_BSpline_Surface.py b/tests/test_BSpline_Surface.py index 99f10c2c..c3e4f52d 100644 --- a/tests/test_BSpline_Surface.py +++ b/tests/test_BSpline_Surface.py @@ -6,6 +6,7 @@ Tests geomdl.BSpline.Surface module. Requires "pytest" to run. """ from geomdl import BSpline +from geomdl import evaluators GEOMDL_DELTA = 0.001 OBJECT_INSTANCE = BSpline.Surface @@ -146,7 +147,7 @@ def test_bspline_surface_eval1(): surf.knotvector_u = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] surf.knotvector_v = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] - # Evaluate curve + # Evaluate surface evalpt = surf.surfpt(u=0.0, v=0.0) assert abs(evalpt[0] - RESULT_LIST[0][0]) < GEOMDL_DELTA @@ -169,7 +170,7 @@ def test_bspline_surface_eval2(): surf.knotvector_u = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] surf.knotvector_v = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] - # Evaluate curve + # Evaluate surface evalpt = surf.surfpt(u=0.0, v=0.2) assert abs(evalpt[0] - RESULT_LIST[1][0]) < GEOMDL_DELTA @@ -215,7 +216,7 @@ def test_bspline_surface_eval4(): surf.knotvector_u = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] surf.knotvector_v = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] - # Evaluate curve + # Evaluate surface evalpt = surf.surfpt(u=0.3, v=0.0) assert abs(evalpt[0] - RESULT_LIST[3][0]) < GEOMDL_DELTA @@ -238,7 +239,7 @@ def test_bspline_surface_eval5(): surf.knotvector_u = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] surf.knotvector_v = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] - # Evaluate curve + # Evaluate surface evalpt = surf.surfpt(u=0.3, v=0.4) assert abs(evalpt[0] - RESULT_LIST[4][0]) < GEOMDL_DELTA @@ -261,7 +262,7 @@ def test_bspline_surface_eval6(): surf.knotvector_u = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] surf.knotvector_v = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] - # Evaluate curve + # Evaluate surface evalpt = surf.surfpt(u=0.3, v=1.0) assert abs(evalpt[0] - RESULT_LIST[5][0]) < GEOMDL_DELTA @@ -284,7 +285,7 @@ def test_bspline_surface_eval7(): surf.knotvector_u = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] surf.knotvector_v = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] - # Evaluate curve + # Evaluate surface evalpt = surf.surfpt(u=0.6, v=0.0) assert abs(evalpt[0] - RESULT_LIST[6][0]) < GEOMDL_DELTA @@ -307,7 +308,7 @@ def test_bspline_surface_eval8(): surf.knotvector_u = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] surf.knotvector_v = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] - # Evaluate curve + # Evaluate surface evalpt = surf.surfpt(u=0.6, v=0.6) assert abs(evalpt[0] - RESULT_LIST[7][0]) < GEOMDL_DELTA @@ -330,7 +331,7 @@ def test_bspline_surface_eval9(): surf.knotvector_u = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] surf.knotvector_v = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] - # Evaluate curve + # Evaluate surface evalpt = surf.surfpt(u=0.6, v=1.0) assert abs(evalpt[0] - RESULT_LIST[8][0]) < GEOMDL_DELTA @@ -353,7 +354,7 @@ def test_bspline_surface_eval10(): surf.knotvector_u = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] surf.knotvector_v = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] - # Evaluate curve + # Evaluate surface evalpt = surf.surfpt(u=1.0, v=0.0) assert abs(evalpt[0] - RESULT_LIST[9][0]) < GEOMDL_DELTA @@ -376,7 +377,7 @@ def test_bspline_surface_eval11(): surf.knotvector_u = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] surf.knotvector_v = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] - # Evaluate curve + # Evaluate surface evalpt = surf.surfpt(u=1.0, v=0.8) assert abs(evalpt[0] - RESULT_LIST[10][0]) < GEOMDL_DELTA @@ -399,7 +400,7 @@ def test_bspline_surface_eval12(): surf.knotvector_u = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] surf.knotvector_v = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] - # Evaluate curve + # Evaluate surface evalpt = surf.surfpt(u=1.0, v=1.0) assert abs(evalpt[0] - RESULT_LIST[11][0]) < GEOMDL_DELTA @@ -407,6 +408,109 @@ def test_bspline_surface_eval12(): assert abs(evalpt[2] - RESULT_LIST[11][2]) < GEOMDL_DELTA +def test_bspline_surface_deriv_ctrlpts(): + test_degree = 3 + test_knotvector = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] + test_u = 0.35 + test_v = 0.35 + test_order = 3 + + # Create a surface isntance + surf = OBJECT_INSTANCE() + + # Set degrees + surf.degree_u = test_degree + surf.degree_v = test_degree + + # Set control points + surf.set_ctrlpts(CONTROL_POINTS, 6, 6) + + # Set knot vectors + surf.knotvector_u = test_knotvector + surf.knotvector_v = test_knotvector + + # Take the derivatives + der1 = surf.derivatives(u=test_v, v=test_u, order=test_order) + + CONTROL_POINTS2D = [list(_) for _ in surf.ctrlpts2d] + + # Compute the control points of the derivative + deriv_ctrlpts = surf.derivatives_ctrlpts(order=test_order - 1) + + for k in range(0, test_order): + for l in range(0, test_order - k): + surfacek = OBJECT_INSTANCE() + surfacek.degree_u = test_degree - k + surfacek.degree_v = test_degree - l + + # Cutting out None values in deriv_ctrlpts[k][l] and excess clamping values in u and v knot vector + if k == 0: + + if l == 0: + kctrlpts2d = [_ for _ in deriv_ctrlpts[k][l]] + kctrlpts = [p for _ in kctrlpts2d for p in _] + surfacek.set_ctrlpts(kctrlpts, 6 - k, 6 - l) + + surfacek.knotvector_u = test_knotvector + surfacek.knotvector_v = test_knotvector + + else: + kctrlpts2d = [_[:-l] for _ in deriv_ctrlpts[k][l]] + kctrlpts = [p for _ in kctrlpts2d for p in _] + surfacek.set_ctrlpts(kctrlpts, 6 - k, 6 - l) + + surfacek.knotvector_u = test_knotvector + surfacek.knotvector_v = test_knotvector[l:-l] + + else: + if l == 0: + kctrlpts2d = [_ for _ in deriv_ctrlpts[k][l]][:-k] + kctrlpts = [p for _ in kctrlpts2d for p in _] + surfacek.set_ctrlpts(kctrlpts, 6 - k, 6 - l) + + surfacek.knotvector_v = test_knotvector + surfacek.knotvector_u = test_knotvector[k:-k] + + else: + kctrlpts2d = [_[:-l] for _ in deriv_ctrlpts[k][l]][:-k] + kctrlpts = [p for _ in kctrlpts2d for p in _] + surfacek.set_ctrlpts(kctrlpts, 6 - k, 6 - l) + + surfacek.knotvector_v = test_knotvector[l:-l] + surfacek.knotvector_u = test_knotvector[k:-k] + + assert abs(surfacek.surfpt(test_u, test_v)[0] - der1[k][l][0]) < GEOMDL_DELTA + assert abs(surfacek.surfpt(test_u, test_v)[1] - der1[k][l][1]) < GEOMDL_DELTA + assert abs(surfacek.surfpt(test_u, test_v)[2] - der1[k][l][2]) < GEOMDL_DELTA + + +def test_bspline_surface_deriv(): + # Create a surface isntance + surf = OBJECT_INSTANCE() + + # Set degrees + surf.degree_u = 3 + surf.degree_v = 3 + + # Set control points + surf.set_ctrlpts(CONTROL_POINTS, 6, 6) + + # Set knot vectors + surf.knotvector_u = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] + surf.knotvector_v = [0.0, 0.0, 0.0, 0.0, 0.33, 0.66, 1.0, 1.0, 1.0, 1.0] + + # Take the derivative + der1 = surf.derivatives(u=0.35, v=0.35, order=2) + surf.evaluator = evaluators.SurfaceEvaluator2() + der2 = surf.derivatives(u=0.35, v=0.35, order=2) + + for k in range(0, 3): + for l in range(0, 3 - k): + assert abs(der1[k][l][0] - der2[k][l][0]) < GEOMDL_DELTA + assert abs(der1[k][l][1] - der2[k][l][1]) < GEOMDL_DELTA + assert abs(der1[k][l][2] - der2[k][l][2]) < GEOMDL_DELTA + + def test_bspline_surface_bbox(): # Create a surface instance surf = OBJECT_INSTANCE() @@ -454,7 +558,7 @@ def test_bspline_surface_insert_knot1(): # Insert knot surf.insert_knot(u=0.3, v=0.4) - # Evaluate curve + # Evaluate surface evalpt = surf.surfpt(u=0.3, v=0.4) assert abs(evalpt[0] - RESULT_LIST[4][0]) < GEOMDL_DELTA @@ -480,7 +584,7 @@ def test_bspline_surface_insert_knot2(): # Insert knot surf.insert_knot(u=0.3, ru=2) - # Evaluate curve + # Evaluate surface evalpt = surf.surfpt(u=0.3, v=0.4) assert abs(evalpt[0] - RESULT_LIST[4][0]) < GEOMDL_DELTA @@ -506,7 +610,7 @@ def test_bspline_surface_insert_knot3(): # Insert knot surf.insert_knot(v=0.3, rv=2) - # Evaluate curve + # Evaluate surface evalpt = surf.surfpt(u=0.3, v=0.4) assert abs(evalpt[0] - RESULT_LIST[4][0]) < GEOMDL_DELTA diff --git a/tests/test_NURBS_Curve2D.py b/tests/test_NURBS_Curve2D.py index 395ad437..5bd8bb4a 100644 --- a/tests/test_NURBS_Curve2D.py +++ b/tests/test_NURBS_Curve2D.py @@ -196,4 +196,4 @@ def test_nurbs_curve2d_insert_knot2(nurbs_curve2): res = [33.304, 24.593] assert abs(evalpt[0] - res[0]) < GEOMDL_DELTA - assert abs(evalpt[1] - res[1]) < GEOMDL_DELTA \ No newline at end of file + assert abs(evalpt[1] - res[1]) < GEOMDL_DELTA