From 3c407ebeaaa9a45be9ae6e84d6dc39451b1ab3a9 Mon Sep 17 00:00:00 2001 From: Laurynas Duburas Date: Thu, 12 Jun 2025 16:22:21 +0200 Subject: [PATCH] Curves: Enhance tesselation of NURBS with corners Current NURBS evaluation handles corners or sharp angles poorly. Sharp edges appear when a knot vector value is repeated `order - 1` times. Users can make sharp corners by creating NURBS curve with `Bezier` knot mode or by setting `order` to 2 for legacy curves. The problem occurs because current algorithm takes all the curve's definition interval, divides it into equal parts and evaluates at those points, but corners are exactly on repeated knot's. To hit those, the resolution has to be increased higher than required for the rest of the curve. The new algorithm divides non zero length intervals between two adjacent knots into equal parts. This way corners are hit with a resolution of 1. This does change the evaluated points of NURBS curves, which is why some test results have to be updated in this commit. Pull Request: https://projects.blender.org/blender/blender/pulls/138565 --- source/blender/blenkernel/BKE_curves.hh | 9 +- .../blender/blenkernel/intern/curve_nurbs.cc | 83 ++++++++++++++++--- .../intern/curve_to_mesh_convert.cc | 5 +- .../blenkernel/intern/curves_geometry.cc | 29 +++++-- .../blenkernel/intern/curves_geometry_test.cc | 21 ++--- tests/files/io_tests/obj/all_curves.obj | 4 +- .../io_tests/obj/all_curves_as_nurbs.obj | 2 +- tests/files/io_tests/obj/all_objects.obj | 2 +- .../io_tests/obj/all_objects_mat_groups.obj | 2 +- tests/files/io_tests/obj/nurbs_mesh.obj | 2 +- .../io_tests/obj/reference/all_curves.txt | 35 ++++---- .../curves/knot_structure.blend | 4 +- .../curves/many_nurbs_curves.blend | 4 +- .../curves/normals_free_nurbs.blend | 4 +- .../curves/set_handle_type.blend | 4 +- .../curves/spline_parameter.blend | 4 +- tests/python/bl_usd_export_test.py | 2 +- 17 files changed, 147 insertions(+), 69 deletions(-) diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index b9a7da58a35..2cc5bd1044d 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -854,8 +854,12 @@ bool check_valid_num_and_order(int points_num, int8_t order, bool cyclic, KnotsM * for predictability and so that cached basis weights of NURBS curves with these properties can be * shared. */ -int calculate_evaluated_num( - int points_num, int8_t order, bool cyclic, int resolution, KnotsMode knots_mode); +int calculate_evaluated_num(int points_num, + int8_t order, + bool cyclic, + int resolution, + KnotsMode knots_mode, + Span knots); /** * Calculate the length of the knot vector for a NURBS curve with the given properties. @@ -906,6 +910,7 @@ Vector calculate_multiplicity_sequence(Span knots); void calculate_basis_cache(int points_num, int evaluated_num, int8_t order, + int resolution, bool cyclic, Span knots, BasisCache &basis_cache); diff --git a/source/blender/blenkernel/intern/curve_nurbs.cc b/source/blender/blenkernel/intern/curve_nurbs.cc index de6703e2929..773150d0c13 100644 --- a/source/blender/blenkernel/intern/curve_nurbs.cc +++ b/source/blender/blenkernel/intern/curve_nurbs.cc @@ -32,16 +32,57 @@ bool check_valid_num_and_order(const int points_num, return true; } +static int calc_nonzero_knot_spans(const int points_num, + const KnotsMode mode, + const int8_t order, + const bool cyclic) +{ + const bool is_bezier = ELEM(mode, NURBS_KNOT_MODE_BEZIER, NURBS_KNOT_MODE_ENDPOINT_BEZIER); + const bool is_end_point = ELEM(mode, NURBS_KNOT_MODE_ENDPOINT, NURBS_KNOT_MODE_ENDPOINT_BEZIER); + /* Inner knots are always repeated once except on Bezier case. */ + const int repeat_inner = is_bezier ? order - 1 : 1; + /* For non endpoint Bezier repeated knots are shifted by one. */ + const int knots_before_geometry = order + int(is_bezier && !is_end_point && order > 2); + const int knots_after_geometry = order - 1 + + (cyclic && mode == NURBS_KNOT_MODE_ENDPOINT ? order - 2 : 0); + + const int knots_total = knots_num(points_num, order, cyclic); + /* On these knots as parameters actual geometry is generated. */ + const int geometry_knots = knots_total - knots_before_geometry - knots_after_geometry; + /* `repeat_inner - 1` is added to `ceil`. */ + const int non_zero_knots = (geometry_knots + repeat_inner - 1) / repeat_inner; + return non_zero_knots; +} + +static int count_nonzero_knot_spans(const int points_num, + const int order, + const bool cyclic, + const Span knots) +{ + BLI_assert(points_num > 0); + const int degree = order - 1; + int span_num = 0; + for (const int knot_span : IndexRange::from_begin_end(cyclic ? 0 : degree, points_num)) { + span_num += (knots[knot_span + 1] - knots[knot_span]) > 0.0f; + } + return span_num; +} + int calculate_evaluated_num(const int points_num, const int8_t order, const bool cyclic, const int resolution, - const KnotsMode knots_mode) + const KnotsMode knots_mode, + const Span knots) { if (!check_valid_num_and_order(points_num, order, cyclic, knots_mode)) { return points_num; } - return resolution * segments_num(points_num, cyclic); + const int nonzero_span_num = knots_mode == KnotsMode::NURBS_KNOT_MODE_CUSTOM && + !knots.is_empty() ? + count_nonzero_knot_spans(points_num, order, cyclic, knots) : + calc_nonzero_knot_spans(points_num, knots_mode, order, cyclic); + return resolution * nonzero_span_num + int(!cyclic); } int knots_num(const int points_num, const int8_t order, const bool cyclic) @@ -206,6 +247,7 @@ static void calculate_basis_for_point(const float parameter, void calculate_basis_cache(const int points_num, const int evaluated_num, const int8_t order, + const int resolution, const bool cyclic, const Span knots, BasisCache &basis_cache) @@ -225,19 +267,34 @@ void calculate_basis_cache(const int points_num, MutableSpan basis_start_indices(basis_cache.start_indices); const int last_control_point_index = cyclic ? points_num + degree : points_num; - const int evaluated_segment_num = segments_num(evaluated_num, cyclic); - const float start = knots[degree]; - const float end = knots[last_control_point_index]; - const float step = (end - start) / evaluated_segment_num; - for (const int i : IndexRange(evaluated_num)) { - /* Clamp parameter due to floating point inaccuracy. */ - const float parameter = std::clamp(start + step * i, knots[0], knots[points_num + degree]); + int eval_point = 0; - MutableSpan point_weights = basis_weights.slice(i * order, order); - - calculate_basis_for_point( - parameter, last_control_point_index, degree, knots, point_weights, basis_start_indices[i]); + for (const int knot_span : IndexRange::from_begin_end(degree, last_control_point_index)) { + const float start = knots[knot_span]; + const float end = knots[knot_span + 1]; + if (start == end) { + continue; + } + const float step_width = (end - start) / resolution; + for (const int step : IndexRange::from_begin_size(0, resolution)) { + const float parameter = start + step * step_width; + calculate_basis_for_point(parameter, + last_control_point_index, + degree, + knots, + basis_weights.slice(eval_point * order, order), + basis_start_indices[eval_point]); + eval_point++; + } + } + if (!cyclic) { + calculate_basis_for_point(knots[last_control_point_index], + last_control_point_index, + degree, + knots, + basis_weights.slice(eval_point * order, order), + basis_start_indices[eval_point]); } } diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index d9a107d000f..c6a7d39ef24 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -501,7 +501,10 @@ static void build_mesh_positions(const CurvesInfo &curves_info, const bool ignore_profile_position = profile_positions.size() == 1 && math::is_equal(profile_positions.first(), float3(0.0f)); if (ignore_profile_position) { - if (mesh.verts_num == curves_info.main.points_num()) { + if (mesh.verts_num == curves_info.main.points_num() && + /* NURBS can have equal evaluated and positions sizes, but different coords. */ + !curves_info.main.has_curve_with_type(CURVE_TYPE_NURBS)) + { const GAttributeReader src = curves_info.main.attributes().lookup("position"); if (src.sharing_info && src.varray.is_span()) { const AttributeInitShared init(src.varray.get_internal_span().data(), *src.sharing_info); diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index c0b2042c265..29feabb0083 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -657,6 +657,8 @@ static void calculate_evaluated_offsets(const CurvesGeometry &curves, const VArray nurbs_orders = curves.nurbs_orders(); const VArray nurbs_knots_modes = curves.nurbs_knots_modes(); + const OffsetIndices custom_knots_by_curve = curves.nurbs_custom_knots_by_curve(); + const Span all_custom_knots = curves.nurbs_custom_knots(); build_offsets(offsets, [&](const int curve_index) -> int { const IndexRange points = points_by_curve[curve_index]; @@ -676,11 +678,17 @@ static void calculate_evaluated_offsets(const CurvesGeometry &curves, return all_bezier_offsets[offsets.last()]; } case CURVE_TYPE_NURBS: - return curves::nurbs::calculate_evaluated_num(points.size(), - nurbs_orders[curve_index], - cyclic[curve_index], - resolution[curve_index], - KnotsMode(nurbs_knots_modes[curve_index])); + const bool is_cyclic = cyclic[curve_index]; + const int8_t order = nurbs_orders[curve_index]; + const KnotsMode knots_mode = KnotsMode(nurbs_knots_modes[curve_index]); + const IndexRange custom_knots_range = custom_knots_by_curve[curve_index]; + const Span custom_knots = knots_mode == NURBS_KNOT_MODE_CUSTOM && + !all_custom_knots.is_empty() && + !custom_knots_range.is_empty() ? + all_custom_knots.slice(custom_knots_range) : + Span(); + return curves::nurbs::calculate_evaluated_num( + points.size(), order, is_cyclic, resolution[curve_index], knots_mode, custom_knots); } BLI_assert_unreachable(); return 0; @@ -755,6 +763,7 @@ void CurvesGeometry::ensure_nurbs_basis_cache() const const OffsetIndices custom_knots_by_curve = this->nurbs_custom_knots_by_curve(); const VArray cyclic = this->cyclic(); const VArray orders = this->nurbs_orders(); + const VArray resolutions = this->resolution(); const VArray knots_modes = this->nurbs_knots_modes(); const Span custom_knots = this->nurbs_custom_knots(); @@ -765,6 +774,7 @@ void CurvesGeometry::ensure_nurbs_basis_cache() const const IndexRange evaluated_points = evaluated_points_by_curve[curve_index]; const int8_t order = orders[curve_index]; + const int resolution = resolutions[curve_index]; const bool is_cyclic = cyclic[curve_index]; const KnotsMode mode = KnotsMode(knots_modes[curve_index]); @@ -782,8 +792,13 @@ void CurvesGeometry::ensure_nurbs_basis_cache() const custom_knots, knots); - curves::nurbs::calculate_basis_cache( - points.size(), evaluated_points.size(), order, is_cyclic, knots, r_data[curve_index]); + curves::nurbs::calculate_basis_cache(points.size(), + evaluated_points.size(), + order, + resolution, + is_cyclic, + knots, + r_data[curve_index]); } }); }); diff --git a/source/blender/blenkernel/intern/curves_geometry_test.cc b/source/blender/blenkernel/intern/curves_geometry_test.cc index e0deafd901a..8a6d0f16204 100644 --- a/source/blender/blenkernel/intern/curves_geometry_test.cc +++ b/source/blender/blenkernel/intern/curves_geometry_test.cc @@ -329,16 +329,17 @@ TEST(curves_geometry, NURBSEvaluation) Span evaluated_positions = curves.evaluated_positions(); static const Array result_1{{ - {0.166667, 0.833333, 0}, {0.150006, 0.815511, 0}, {0.134453, 0.796582, 0}, - {0.119924, 0.776627, 0}, {0.106339, 0.75573, 0}, {0.0936146, 0.733972, 0}, - {0.0816693, 0.711434, 0}, {0.0704211, 0.6882, 0}, {0.0597879, 0.66435, 0}, - {0.0496877, 0.639968, 0}, {0.0400385, 0.615134, 0}, {0.0307584, 0.589931, 0}, - {0.0217653, 0.564442, 0}, {0.0129772, 0.538747, 0}, {0.00431208, 0.512929, 0}, - {-0.00431208, 0.487071, 0}, {-0.0129772, 0.461253, 0}, {-0.0217653, 0.435558, 0}, - {-0.0307584, 0.410069, 0}, {-0.0400385, 0.384866, 0}, {-0.0496877, 0.360032, 0}, - {-0.0597878, 0.33565, 0}, {-0.0704211, 0.3118, 0}, {-0.0816693, 0.288566, 0}, - {-0.0936146, 0.266028, 0}, {-0.106339, 0.24427, 0}, {-0.119924, 0.223373, 0}, - {-0.134453, 0.203418, 0}, {-0.150006, 0.184489, 0}, {-0.166667, 0.166667, 0}, + {0.166667, 0.833333, 0}, + {0.121333, 0.778667, 0}, + {0.084, 0.716, 0}, + {0.0526667, 0.647333, 0}, + {0.0253333, 0.574667, 0}, + {0, 0.5, 0}, + {-0.0253333, 0.425333, 0}, + {-0.0526667, 0.352667, 0}, + {-0.084, 0.284, 0}, + {-0.121333, 0.221333, 0}, + {-0.166667, 0.166667, 0}, }}; for (const int i : evaluated_positions.index_range()) { EXPECT_V3_NEAR(evaluated_positions[i], result_1[i], 1e-5f); diff --git a/tests/files/io_tests/obj/all_curves.obj b/tests/files/io_tests/obj/all_curves.obj index 6e894c04223..c62588a7263 100644 --- a/tests/files/io_tests/obj/all_curves.obj +++ b/tests/files/io_tests/obj/all_curves.obj @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5b265d844db7a6f84520e6c01987eec91eab20bd4eeb3e061e83e6b4a35d4323 -size 8652 +oid sha256:550fa013ce1c048e889087fcbda18c8043456cca3759375064e1b4c8111308be +size 7531 diff --git a/tests/files/io_tests/obj/all_curves_as_nurbs.obj b/tests/files/io_tests/obj/all_curves_as_nurbs.obj index 16007e73e3a..b97065949f2 100644 --- a/tests/files/io_tests/obj/all_curves_as_nurbs.obj +++ b/tests/files/io_tests/obj/all_curves_as_nurbs.obj @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:72b89d5470bb42af366946d1d32cc653001facae1266ea6342f1609a190e55a9 +oid sha256:c2098e5dce32abb74d49e49f71479ecdd7a9acfe4f86b699e36584c4103fc4ba size 6709 diff --git a/tests/files/io_tests/obj/all_objects.obj b/tests/files/io_tests/obj/all_objects.obj index 8515098238e..c620d038b5c 100644 --- a/tests/files/io_tests/obj/all_objects.obj +++ b/tests/files/io_tests/obj/all_objects.obj @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d11c9cb2b0299b3d425864f1f583a7de2f4152306d7ed886cdcffb829bf93a8 +oid sha256:f3cc276cb772ecef16c332de06fce6c5bfc577fabdea07a8d3b6293f8727bdfe size 321069 diff --git a/tests/files/io_tests/obj/all_objects_mat_groups.obj b/tests/files/io_tests/obj/all_objects_mat_groups.obj index 590022fd085..d6fc059015d 100644 --- a/tests/files/io_tests/obj/all_objects_mat_groups.obj +++ b/tests/files/io_tests/obj/all_objects_mat_groups.obj @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be872ec81f8cbb50121b40c5de90cb3ca7e19ff1eb1421ba8ad27fa4681473f6 +oid sha256:c3a4e24475815b526f6fc180c9d0016a482434e822a3270846ecda9e103a60c7 size 321239 diff --git a/tests/files/io_tests/obj/nurbs_mesh.obj b/tests/files/io_tests/obj/nurbs_mesh.obj index 7b4d2dea28d..84654ba12a2 100644 --- a/tests/files/io_tests/obj/nurbs_mesh.obj +++ b/tests/files/io_tests/obj/nurbs_mesh.obj @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7da3b2cb989d0fe71a95a4e32b302b538a7baf406d2728d7b5d2265c49cd8c04 +oid sha256:98389651f47f3d6b2f5eca34716d8151a7c6a033e379a9cdc03219514fe034f8 size 4213 diff --git a/tests/files/io_tests/obj/reference/all_curves.txt b/tests/files/io_tests/obj/reference/all_curves.txt index bb2877e61e7..cbb5cd2c6f1 100644 --- a/tests/files/io_tests/obj/reference/all_curves.txt +++ b/tests/files/io_tests/obj/reference/all_curves.txt @@ -65,37 +65,34 @@ - (11.193, 0.000, 0.969) - (10.989, 0.000, 0.883) -- Mesh 'NurbsCurve' vtx:9 face:0 loop:0 edge:8 - - 0/1 1/2 2/3 3/4 4/5 5/6 6/7 7/8 +- Mesh 'NurbsCurve' vtx:4 face:0 loop:0 edge:3 + - 0/1 1/2 2/3 - attr 'position' FLOAT_VECTOR POINT - (7.730, 0.000, -0.375) - - (7.886, 0.000, -0.581) - - (8.063, 0.000, -0.734) - ... - - (8.846, 0.000, -0.920) - - (9.030, 0.000, -0.887) + - (8.189, 0.000, -0.809) + - (8.717, 0.000, -0.927) - (9.197, 0.000, -0.833) -- Mesh 'NurbsCurve2' vtx:24 face:0 loop:0 edge:23 - - 0/1 1/2 2/3 3/4 4/5 ... 18/19 19/20 20/21 21/22 22/23 +- Mesh 'NurbsCurve2' vtx:17 face:0 loop:0 edge:16 + - 0/1 1/2 2/3 3/4 4/5 ... 11/12 12/13 13/14 14/15 15/16 - attr 'position' FLOAT_VECTOR POINT - (5.857, 0.000, 5.211) - - (5.883, 0.000, 4.843) - - (5.990, 0.000, 4.491) + - (5.921, 0.000, 4.685) + - (6.135, 0.000, 4.218) ... - - (9.419, 0.000, 3.357) - - (9.557, 0.000, 3.570) + - (9.283, 0.000, 3.248) + - (9.500, 0.000, 3.465) - (9.665, 0.000, 3.871) -- Mesh 'NurbsPathCurve' vtx:31 face:0 loop:0 edge:29 - - 0/1 1/2 2/3 3/4 4/5 ... 25/26 26/27 27/28 28/29 29/30 +- Mesh 'NurbsPathCurve' vtx:15 face:0 loop:0 edge:13 + - 0/1 1/2 2/3 3/4 4/5 ... 9/10 10/11 11/12 12/13 13/14 - attr 'position' FLOAT_VECTOR POINT - (13.691, 0.000, 0.000) - - (14.059, 0.000, 0.419) - - (14.383, 0.000, 0.670) + - (14.345, 0.000, 0.646) + - (14.865, 0.000, 0.823) ... - - (16.242, 0.000, -0.671) - - (16.339, 0.000, -0.583) + - (15.852, 0.000, -0.877) + - (16.160, 0.000, -0.732) - (16.430, 0.000, -0.479) - Mesh 'PolyCircle' vtx:4 face:0 loop:0 edge:4 diff --git a/tests/files/modeling/geometry_nodes/curves/knot_structure.blend b/tests/files/modeling/geometry_nodes/curves/knot_structure.blend index 8f998616679..7a9d246de3e 100644 --- a/tests/files/modeling/geometry_nodes/curves/knot_structure.blend +++ b/tests/files/modeling/geometry_nodes/curves/knot_structure.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5a8eda24804f2c3ac01e93af91af20b45ccbf57fee6bf16b81abc47ce781eb99 -size 145664 +oid sha256:6393161f668410636448320997eabecaff0d2174bcbec6e2b580cac62487b8f1 +size 1603172 diff --git a/tests/files/modeling/geometry_nodes/curves/many_nurbs_curves.blend b/tests/files/modeling/geometry_nodes/curves/many_nurbs_curves.blend index ccd4bfd9f66..605c3b4558e 100644 --- a/tests/files/modeling/geometry_nodes/curves/many_nurbs_curves.blend +++ b/tests/files/modeling/geometry_nodes/curves/many_nurbs_curves.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:19a9d325f95220e8ddc3a787a02d18a78480a6a43ad7177d128f069bc6163dbf -size 150542 +oid sha256:fc4ccfecbac1c16487705e4d62137f40874df01f1606a2912d25d01f38fba768 +size 991954 diff --git a/tests/files/modeling/geometry_nodes/curves/normals_free_nurbs.blend b/tests/files/modeling/geometry_nodes/curves/normals_free_nurbs.blend index 2c479992366..c32187b97be 100644 --- a/tests/files/modeling/geometry_nodes/curves/normals_free_nurbs.blend +++ b/tests/files/modeling/geometry_nodes/curves/normals_free_nurbs.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:81af832bacc4e62ad28785a998d1b3a09b555058466553481ce30434b041d402 -size 89539 +oid sha256:14b15a0a23f21cfefa67b3b437ae68615464a1275c354ca9b6c92b98f388d584 +size 642331 diff --git a/tests/files/modeling/geometry_nodes/curves/set_handle_type.blend b/tests/files/modeling/geometry_nodes/curves/set_handle_type.blend index 75ee0d36351..a6d831dd05d 100644 --- a/tests/files/modeling/geometry_nodes/curves/set_handle_type.blend +++ b/tests/files/modeling/geometry_nodes/curves/set_handle_type.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6346756e7672b3330f0564518c98419d2616ab5d221e0afa4066397e733db118 -size 135396 +oid sha256:8bdd3fb883c784f7dfe59ab11315596f577f537cb429ca23a7229f1ef5bd0cbb +size 1014822 diff --git a/tests/files/modeling/geometry_nodes/curves/spline_parameter.blend b/tests/files/modeling/geometry_nodes/curves/spline_parameter.blend index a0af4cd52cd..78f1d87faa1 100644 --- a/tests/files/modeling/geometry_nodes/curves/spline_parameter.blend +++ b/tests/files/modeling/geometry_nodes/curves/spline_parameter.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ed3a58f5c685a7dbae64b7cfaee3170b90eced80ea77061ef790f5e9c7b94dd -size 124628 +oid sha256:5bca0f399a4a8eab1d3e9ace766c1acc55f8b1b5c542020a84b7cd3db5876f12 +size 1060327 diff --git a/tests/python/bl_usd_export_test.py b/tests/python/bl_usd_export_test.py index 84eeb455290..a780f2ba518 100644 --- a/tests/python/bl_usd_export_test.py +++ b/tests/python/bl_usd_export_test.py @@ -936,7 +936,7 @@ class USDExportTest(AbstractUSDTest): # Contains 2 NURBS curves curve = UsdGeom.NurbsCurves(stage.GetPrimAtPath("/root/NurbsCurve/NurbsCurve")) - check_nurbs_curve(curve, False, [4, 4], [6, 6], 10, [[-1.75, -2.6898, -1.0117], [3.0896, 1.9583, 1.0293]]) + check_nurbs_curve(curve, False, [4, 4], [6, 6], 10, [[-1.75, -2.6891, -1.0117], [3.0896, 1.9583, 1.0293]]) # Contains 1 NURBS curve curve = UsdGeom.NurbsCurves(stage.GetPrimAtPath("/root/NurbsCircle/NurbsCircle"))