From 1883e5feaf0d9e8f7d59417d2e16853f47cc9a53 Mon Sep 17 00:00:00 2001 From: Ryan Chew Date: Wed, 14 Aug 2024 04:01:17 -0700 Subject: [PATCH 1/3] Implement segmenting of Line2D for better fidelity when using width curves --- doc/classes/Line2D.xml | 3 ++ scene/2d/line_2d.cpp | 66 +++++++++++++++++++++++++++++++++++++-- scene/2d/line_2d.h | 5 +++ scene/2d/line_builder.cpp | 21 +++++++------ scene/2d/line_builder.h | 3 +- 5 files changed, 85 insertions(+), 13 deletions(-) diff --git a/doc/classes/Line2D.xml b/doc/classes/Line2D.xml index a553e79746e0..0b6e4fd038e8 100644 --- a/doc/classes/Line2D.xml +++ b/doc/classes/Line2D.xml @@ -70,6 +70,9 @@ [b]Note:[/b] The shape of the closing segment is not guaranteed to be seamless if a [member width_curve] is provided. [b]Note:[/b] The joint between the closing segment and the first segment is drawn first and it samples the [member gradient] and the [member width_curve] at the beginning. This is an implementation detail that might change in a future version. + + The horizontal offset value used when sampling from the width curve. Typically ranges from -1.0 to 1.0. + The color of the polyline. Will not be used if a gradient is set. diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index be58875e0e7c..d16850addbc8 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -119,6 +119,11 @@ Ref Line2D::get_curve() const { return _curve; } +void Line2D::set_curve_offset(float curve_offset) { + _curve_offset = curve_offset; + queue_redraw(); +} + Vector Line2D::get_points() const { return _points; } @@ -277,9 +282,61 @@ void Line2D::_draw() { } // TODO Maybe have it as member rather than copying parameters and allocating memory? + + int num_segments = _curve.is_valid() ? _curve->get_bake_resolution() : -1; + if (static_cast(_generated_draw_points.get_capacity()) < num_segments) + _generated_draw_points.reserve(num_segments); + _generated_draw_points.clear(); + + bool just_use_points = num_segments == -1; + // We have less segments than what's defined, so we just use the points. + if (just_use_points) { + _generated_draw_points = _points; + } else { + // Set the first and last points from our line data. + _generated_draw_points.push_back(_points[0]); + + // TODO: Cache the memory somewhere so we don't keep allocating memory + int line_count = _closed ? len : len - 1; + LocalVector dists_between_points; + LocalVector line_directions; + dists_between_points.reserve(line_count); + line_directions.reserve(line_count); + + // Gather the total line length, with distances and directions for each sub-line + float total_line_length = 0.0f; + for (int i = 0; i < line_count; ++i) { + Vector2 line_dir = _points[(i + 1) % len] - _points[i]; + float distance_bt_points = line_dir.length(); + total_line_length += distance_bt_points; + dists_between_points.push_back(distance_bt_points); + line_directions.push_back(line_dir / distance_bt_points); + } + + // Can't have negative segments! + float segment_length = total_line_length / MAX(1, static_cast(num_segments)); + for (int s = 0; s < line_count; ++s) { + int segments_for_line = static_cast(Math::ceil(dists_between_points[s] / segment_length)); + + Vector2 line_start = _points[s]; + Vector2 line_end = _points[(s + 1) % len]; + float line_distance = dists_between_points[s]; + _generated_draw_points.push_back(_points[s]); + + if (segments_for_line > 1) { + for (int l = 1; l < segments_for_line; ++l) { + float current_length_from_line_start = segment_length * static_cast(l); + _generated_draw_points.push_back(line_start + (line_end - line_start) * (current_length_from_line_start / line_distance)); + } + } + } + // Set last point. + _generated_draw_points.push_back(_points[line_count % len]); + } + LineBuilder lb; - lb.points = _points; - lb.closed = _closed; + lb.closed = _closed && just_use_points; + lb.points = &_generated_draw_points; lb.default_color = _default_color; lb.gradient = *_gradient; lb.texture_mode = _texture_mode; @@ -290,6 +347,7 @@ void Line2D::_draw() { lb.sharp_limit = _sharp_limit; lb.width = _width; lb.curve = *_curve; + lb.curve_offset = _curve_offset; RID texture_rid; if (_texture.is_valid()) { @@ -358,6 +416,9 @@ void Line2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_curve", "curve"), &Line2D::set_curve); ClassDB::bind_method(D_METHOD("get_curve"), &Line2D::get_curve); + ClassDB::bind_method(D_METHOD("set_curve_offset", "curve_offset"), &Line2D::set_curve_offset); + ClassDB::bind_method(D_METHOD("get_curve_offset"), &Line2D::get_curve_offset); + ClassDB::bind_method(D_METHOD("set_default_color", "color"), &Line2D::set_default_color); ClassDB::bind_method(D_METHOD("get_default_color"), &Line2D::get_default_color); @@ -392,6 +453,7 @@ void Line2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "closed"), "set_closed", "is_closed"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width", PROPERTY_HINT_NONE, "suffix:px"), "set_width", "get_width"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "width_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "curve_offset", PROPERTY_HINT_RANGE, "-1.0,1.0,0.01"), "set_curve_offset", "get_curve_offset"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "default_color"), "set_default_color", "get_default_color"); ADD_GROUP("Fill", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gradient", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_gradient", "get_gradient"); diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h index 1d750ca456c9..431120502a94 100644 --- a/scene/2d/line_2d.h +++ b/scene/2d/line_2d.h @@ -85,6 +85,9 @@ class Line2D : public Node2D { void set_curve(const Ref &curve); Ref get_curve() const; + void set_curve_offset(float curve_offset); + float get_curve_offset() const { return _curve_offset; } + void set_default_color(Color color); Color get_default_color() const; @@ -127,12 +130,14 @@ class Line2D : public Node2D { private: Vector _points; + LocalVector _generated_draw_points; LineJointMode _joint_mode = LINE_JOINT_SHARP; LineCapMode _begin_cap_mode = LINE_CAP_NONE; LineCapMode _end_cap_mode = LINE_CAP_NONE; bool _closed = false; float _width = 10.0; Ref _curve; + float _curve_offset = 0.0f; Color _default_color = Color(1, 1, 1); Ref _gradient; Ref _texture; diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp index f8a8aa487c36..17757cdea55c 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -44,7 +44,7 @@ LineBuilder::LineBuilder() { void LineBuilder::build() { // Need at least 2 points to draw a line, so clear the output and return. - if (points.size() < 2) { + if (points->size() < 2) { vertices.clear(); colors.clear(); indices.clear(); @@ -57,7 +57,7 @@ void LineBuilder::build() { const float hw = width / 2.f; const float hw_sq = hw * hw; const float sharp_limit_sq = sharp_limit * sharp_limit; - const int point_count = points.size(); + const int point_count = static_cast(points->size()); const bool wrap_around = closed && point_count > 2; _interpolate_color = gradient != nullptr; @@ -68,8 +68,8 @@ void LineBuilder::build() { // Initial values - Vector2 pos0 = points[0]; - Vector2 pos1 = points[1]; + Vector2 pos0 = (*points)[0]; + Vector2 pos1 = (*points)[1]; Vector2 f0 = (pos1 - pos0).normalized(); Vector2 u0 = f0.orthogonal(); Vector2 pos_up0 = pos0; @@ -92,10 +92,10 @@ void LineBuilder::build() { if (distance_required) { // Calculate the total distance. for (int i = 1; i < point_count; ++i) { - total_distance += points[i].distance_to(points[i - 1]); + total_distance += (*points)[i].distance_to((*points)[i - 1]); } if (wrap_around) { - total_distance += points[point_count - 1].distance_to(pos0); + total_distance += (*points)[point_count - 1].distance_to(pos0); } else { // Adjust the total distance. // The line's outer length may be a little higher due to the end caps. @@ -172,8 +172,8 @@ void LineBuilder::build() { // For each additional segment for (int i = first_point; i <= segments_count; ++i) { - pos1 = points[(i == -1) ? point_count - 1 : i % point_count]; // First point. - Vector2 pos2 = points[(i + 1) % point_count]; // Second point. + pos1 = (*points)[(i == -1) ? point_count - 1 : i % point_count]; // First point. + Vector2 pos2 = (*points)[(i + 1) % point_count]; // Second point. Vector2 f1 = (pos2 - pos1).normalized(); Vector2 u1 = f1.orthogonal(); @@ -189,7 +189,8 @@ void LineBuilder::build() { color1 = gradient->get_color_at_offset(current_distance1 / total_distance); } if (retrieve_curve) { - width_factor = curve->sample_baked(current_distance1 / total_distance); + float offset = CLAMP((current_distance1 / total_distance) - curve_offset, 0.0f, 1.0f); + width_factor = curve->sample_baked(offset); modified_hw = hw * width_factor; } @@ -379,7 +380,7 @@ void LineBuilder::build() { // Draw the last (or only) segment, with its end cap logic. if (!wrap_around) { - pos1 = points[point_count - 1]; + pos1 = (*points)[point_count - 1]; if (distance_required) { current_distance1 += pos0.distance_to(pos1); diff --git a/scene/2d/line_builder.h b/scene/2d/line_builder.h index 3706352d5c7c..c5638a02c179 100644 --- a/scene/2d/line_builder.h +++ b/scene/2d/line_builder.h @@ -37,13 +37,14 @@ class LineBuilder { public: // TODO Move in a struct and reference it // Input - Vector points; + LocalVector *points; Line2D::LineJointMode joint_mode = Line2D::LINE_JOINT_SHARP; Line2D::LineCapMode begin_cap_mode = Line2D::LINE_CAP_NONE; Line2D::LineCapMode end_cap_mode = Line2D::LINE_CAP_NONE; bool closed = false; float width = 10.0; Curve *curve = nullptr; + float curve_offset = 0.0f; Color default_color = Color(0.4, 0.5, 1); Gradient *gradient = nullptr; Line2D::LineTextureMode texture_mode = Line2D::LineTextureMode::LINE_TEXTURE_NONE; From dc8a5a7b460aa9f1eb45fab3c7d2d4248269c0c5 Mon Sep 17 00:00:00 2001 From: Ryan Chew Date: Sat, 17 Aug 2024 18:34:50 -0700 Subject: [PATCH 2/3] Implement Line2D gradient fix and additional curve segment property --- doc/classes/Line2D.xml | 4 ++ scene/2d/line_2d.cpp | 143 +++++++++++++++++++++++++++----------- scene/2d/line_2d.h | 5 +- scene/2d/line_builder.cpp | 24 +++---- scene/2d/line_builder.h | 2 +- scene/resources/curve.cpp | 1 + 6 files changed, 125 insertions(+), 54 deletions(-) diff --git a/doc/classes/Line2D.xml b/doc/classes/Line2D.xml index 0b6e4fd038e8..768aa469a3fd 100644 --- a/doc/classes/Line2D.xml +++ b/doc/classes/Line2D.xml @@ -70,6 +70,10 @@ [b]Note:[/b] The shape of the closing segment is not guaranteed to be seamless if a [member width_curve] is provided. [b]Note:[/b] The joint between the closing segment and the first segment is drawn first and it samples the [member gradient] and the [member width_curve] at the beginning. This is an implementation detail that might change in a future version. + + Subdivide the line into the number of [member curve_line_segments] at minimum. The resulting segment count varies depending on the use of a fill gradient and the distances between points. + [b]Note:[/b] Using higher values results in more segments (and vertices/indices) that need to be generated which can degrade performance, particularly if you are animating properties on your line. + The horizontal offset value used when sampling from the width curve. Typically ranges from -1.0 to 1.0. diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index d16850addbc8..014389062b8e 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -119,6 +119,11 @@ Ref Line2D::get_curve() const { return _curve; } +void Line2D::set_curve_line_segments(int curve_line_segments) { + _curve_line_segments = curve_line_segments; + queue_redraw(); +} + void Line2D::set_curve_offset(float curve_offset) { _curve_offset = curve_offset; queue_redraw(); @@ -281,62 +286,116 @@ void Line2D::_draw() { return; } - // TODO Maybe have it as member rather than copying parameters and allocating memory? + // NOTE: + // Sublines refer to the lines between points before curve segmentation. + + LineBuilder lb; + LocalVector &generated_draw_points = lb.points; + LocalVector gradient_inclusive_points; + // We insert gradient points that correspond to a position on our normalized line (e.g. gradient point of 0.5 means we add a point in the at the half-way point of our total line). + if (_gradient.is_valid()) { + int gradient_point_count = _gradient->get_point_count(); - int num_segments = _curve.is_valid() ? _curve->get_bake_resolution() : -1; - if (static_cast(_generated_draw_points.get_capacity()) < num_segments) - _generated_draw_points.reserve(num_segments); - _generated_draw_points.clear(); + int subline_count = len - 1; + LocalVector subline_lengths; + LocalVector sublines; + subline_lengths.reserve(subline_count); + sublines.reserve(len + _gradient->get_point_count() - 1); + + // Gather the total line length, and distances and directions for each subline + float total_line_length = 0.0f; + for (int i = 0; i < subline_count; ++i) { + Vector2 subline_dir = _points[i + 1] - _points[i]; + float subline_length = subline_dir.length(); + total_line_length += subline_length; + subline_lengths.push_back(subline_length); + sublines.push_back(subline_dir); + } + + // Reserve the right amount of memory so we're not constantly reallocating as we're pushing items in the back. + gradient_inclusive_points.reserve(static_cast(_points.size()) + static_cast(gradient_point_count)); + + int gradient_idx = 0; + + float cumulative_line_length = 0.0f; + for (int line_idx = 0; line_idx < subline_count; ++line_idx) { + // All current calculations are based on the current subline. + int wrapped_line_idx = line_idx % static_cast(_points.size()); + const Vector2 &curr_subline_start = _points[wrapped_line_idx]; + const Vector2 &curr_subline = sublines[wrapped_line_idx]; + const float &curr_subline_length = subline_lengths[wrapped_line_idx]; + + // This is where our current subline points lay on our normalized line scale. + float curr_subline_start_offset = cumulative_line_length / total_line_length; + float curr_subline_end_offset = (cumulative_line_length + curr_subline_length) / total_line_length; + + gradient_inclusive_points.push_back(curr_subline_start); + // We check to see if there's a gradient point that has an offset between our current subline. + // For the curve offset, we only care about the X axis that ranges from 0 to 1, corresponding with our line normalization. + while (gradient_idx < _gradient->get_point_count()) { + float curr_gradient_point_offset = _gradient->get_offset(gradient_idx); + float relative_gradient_point_offset = (curr_gradient_point_offset - curr_subline_start_offset) / (curr_subline_end_offset - curr_subline_start_offset); + // Only add a point if the point offset is between our subline start and end. + if (relative_gradient_point_offset >= 1.0f) { + break; + } + if (relative_gradient_point_offset > 0.0f) { + gradient_inclusive_points.push_back(curr_subline_start + curr_subline * relative_gradient_point_offset); + } + ++gradient_idx; + } + cumulative_line_length += curr_subline_length; + } + gradient_inclusive_points.push_back(_points[subline_count]); + } else { + gradient_inclusive_points = _points; + } + + int subline_count = static_cast(gradient_inclusive_points.size()) - 1; + int num_segments = _curve.is_valid() ? _curve_line_segments : -1; + num_segments = MAX(subline_count, num_segments); + generated_draw_points.reserve(num_segments); + generated_draw_points.clear(); - bool just_use_points = num_segments == -1; // We have less segments than what's defined, so we just use the points. - if (just_use_points) { - _generated_draw_points = _points; + if (num_segments <= subline_count) { + generated_draw_points = gradient_inclusive_points; } else { - // Set the first and last points from our line data. - _generated_draw_points.push_back(_points[0]); + len = gradient_inclusive_points.size(); // TODO: Cache the memory somewhere so we don't keep allocating memory int line_count = _closed ? len : len - 1; - LocalVector dists_between_points; - LocalVector line_directions; - dists_between_points.reserve(line_count); - line_directions.reserve(line_count); + LocalVector subline_lengths; + subline_lengths.reserve(line_count); - // Gather the total line length, with distances and directions for each sub-line + // Gather the total line length, and distances and directions for each segment float total_line_length = 0.0f; - for (int i = 0; i < line_count; ++i) { - Vector2 line_dir = _points[(i + 1) % len] - _points[i]; - float distance_bt_points = line_dir.length(); - total_line_length += distance_bt_points; - dists_between_points.push_back(distance_bt_points); - line_directions.push_back(line_dir / distance_bt_points); + for (int line_idx = 0; line_idx < line_count; ++line_idx) { + Vector2 line_dir = gradient_inclusive_points[(line_idx + 1) % len] - gradient_inclusive_points[line_idx]; + float subline_length = line_dir.length(); + total_line_length += subline_length; + subline_lengths.push_back(subline_length); } - // Can't have negative segments! float segment_length = total_line_length / MAX(1, static_cast(num_segments)); - for (int s = 0; s < line_count; ++s) { - int segments_for_line = static_cast(Math::ceil(dists_between_points[s] / segment_length)); - - Vector2 line_start = _points[s]; - Vector2 line_end = _points[(s + 1) % len]; - float line_distance = dists_between_points[s]; - _generated_draw_points.push_back(_points[s]); - - if (segments_for_line > 1) { - for (int l = 1; l < segments_for_line; ++l) { - float current_length_from_line_start = segment_length * static_cast(l); - _generated_draw_points.push_back(line_start + (line_end - line_start) * (current_length_from_line_start / line_distance)); - } + for (int line_idx = 0; line_idx < line_count; ++line_idx) { + float subline_length = subline_lengths[line_idx]; + int subline_segments_count = static_cast(Math::ceil(subline_length / segment_length)); + Vector2 subline_start = gradient_inclusive_points[line_idx]; + Vector2 subline_end = gradient_inclusive_points[(line_idx + 1) % len]; + generated_draw_points.push_back(gradient_inclusive_points[line_idx]); + for (int l = 1; l < subline_segments_count; ++l) { + float current_segment_relative_start = segment_length * static_cast(l); + generated_draw_points.push_back(subline_start + (subline_end - subline_start) * (current_segment_relative_start / subline_length)); } } - // Set last point. - _generated_draw_points.push_back(_points[line_count % len]); + + // We don't add the last point so that the line builder can properly cap the ends + if (!_closed) + generated_draw_points.push_back(gradient_inclusive_points[line_count % len]); } - LineBuilder lb; - lb.closed = _closed && just_use_points; - lb.points = &_generated_draw_points; + lb.closed = _closed; lb.default_color = _default_color; lb.gradient = *_gradient; lb.texture_mode = _texture_mode; @@ -416,6 +475,9 @@ void Line2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_curve", "curve"), &Line2D::set_curve); ClassDB::bind_method(D_METHOD("get_curve"), &Line2D::get_curve); + ClassDB::bind_method(D_METHOD("set_curve_line_segments", "curve_offset"), &Line2D::set_curve_line_segments); + ClassDB::bind_method(D_METHOD("get_curve_line_segments"), &Line2D::get_curve_line_segments); + ClassDB::bind_method(D_METHOD("set_curve_offset", "curve_offset"), &Line2D::set_curve_offset); ClassDB::bind_method(D_METHOD("get_curve_offset"), &Line2D::get_curve_offset); @@ -453,6 +515,7 @@ void Line2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "closed"), "set_closed", "is_closed"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width", PROPERTY_HINT_NONE, "suffix:px"), "set_width", "get_width"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "width_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "curve_line_segments", PROPERTY_HINT_RANGE, "1,64,1,or_greater"), "set_curve_line_segments", "get_curve_line_segments"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "curve_offset", PROPERTY_HINT_RANGE, "-1.0,1.0,0.01"), "set_curve_offset", "get_curve_offset"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "default_color"), "set_default_color", "get_default_color"); ADD_GROUP("Fill", ""); diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h index 431120502a94..6d6246f6485e 100644 --- a/scene/2d/line_2d.h +++ b/scene/2d/line_2d.h @@ -85,6 +85,9 @@ class Line2D : public Node2D { void set_curve(const Ref &curve); Ref get_curve() const; + void set_curve_line_segments(int curve_line_segments); + int get_curve_line_segments() const { return _curve_line_segments; } + void set_curve_offset(float curve_offset); float get_curve_offset() const { return _curve_offset; } @@ -130,13 +133,13 @@ class Line2D : public Node2D { private: Vector _points; - LocalVector _generated_draw_points; LineJointMode _joint_mode = LINE_JOINT_SHARP; LineCapMode _begin_cap_mode = LINE_CAP_NONE; LineCapMode _end_cap_mode = LINE_CAP_NONE; bool _closed = false; float _width = 10.0; Ref _curve; + int _curve_line_segments = 1; float _curve_offset = 0.0f; Color _default_color = Color(1, 1, 1); Ref _gradient; diff --git a/scene/2d/line_builder.cpp b/scene/2d/line_builder.cpp index 17757cdea55c..a5920d5a2759 100644 --- a/scene/2d/line_builder.cpp +++ b/scene/2d/line_builder.cpp @@ -44,7 +44,7 @@ LineBuilder::LineBuilder() { void LineBuilder::build() { // Need at least 2 points to draw a line, so clear the output and return. - if (points->size() < 2) { + if (points.size() < 2) { vertices.clear(); colors.clear(); indices.clear(); @@ -57,7 +57,7 @@ void LineBuilder::build() { const float hw = width / 2.f; const float hw_sq = hw * hw; const float sharp_limit_sq = sharp_limit * sharp_limit; - const int point_count = static_cast(points->size()); + const int point_count = static_cast(points.size()); const bool wrap_around = closed && point_count > 2; _interpolate_color = gradient != nullptr; @@ -68,8 +68,8 @@ void LineBuilder::build() { // Initial values - Vector2 pos0 = (*points)[0]; - Vector2 pos1 = (*points)[1]; + Vector2 pos0 = points[0]; + Vector2 pos1 = points[1]; Vector2 f0 = (pos1 - pos0).normalized(); Vector2 u0 = f0.orthogonal(); Vector2 pos_up0 = pos0; @@ -92,10 +92,10 @@ void LineBuilder::build() { if (distance_required) { // Calculate the total distance. for (int i = 1; i < point_count; ++i) { - total_distance += (*points)[i].distance_to((*points)[i - 1]); + total_distance += points[i].distance_to(points[i - 1]); } if (wrap_around) { - total_distance += (*points)[point_count - 1].distance_to(pos0); + total_distance += points[point_count - 1].distance_to(pos0); } else { // Adjust the total distance. // The line's outer length may be a little higher due to the end caps. @@ -113,7 +113,7 @@ void LineBuilder::build() { } if (_interpolate_color) { - color0 = gradient->get_color(0); + color0 = gradient->get_color_at_offset(0.0f); } else { colors.push_back(default_color); } @@ -172,8 +172,8 @@ void LineBuilder::build() { // For each additional segment for (int i = first_point; i <= segments_count; ++i) { - pos1 = (*points)[(i == -1) ? point_count - 1 : i % point_count]; // First point. - Vector2 pos2 = (*points)[(i + 1) % point_count]; // Second point. + pos1 = points[(i == -1) ? point_count - 1 : i % point_count]; // First point. + Vector2 pos2 = points[(i + 1) % point_count]; // Second point. Vector2 f1 = (pos2 - pos1).normalized(); Vector2 u1 = f1.orthogonal(); @@ -380,13 +380,13 @@ void LineBuilder::build() { // Draw the last (or only) segment, with its end cap logic. if (!wrap_around) { - pos1 = (*points)[point_count - 1]; + pos1 = points[point_count - 1]; if (distance_required) { current_distance1 += pos0.distance_to(pos1); } if (_interpolate_color) { - color1 = gradient->get_color(gradient->get_point_count() - 1); + color1 = gradient->get_color_at_offset(1.0f); } if (retrieve_curve) { width_factor = curve->sample_baked(1.f); @@ -415,7 +415,7 @@ void LineBuilder::build() { // Custom drawing for a round end cap. if (end_cap_mode == Line2D::LINE_CAP_ROUND) { // Note: color is not used in case we don't interpolate. - Color color = _interpolate_color ? gradient->get_color(gradient->get_point_count() - 1) : Color(0, 0, 0); + Color color = _interpolate_color ? gradient->get_color_at_offset(1.0f) : Color(0, 0, 0); float dist = 0; if (texture_mode == Line2D::LINE_TEXTURE_TILE) { dist = width_factor / tile_aspect; diff --git a/scene/2d/line_builder.h b/scene/2d/line_builder.h index c5638a02c179..e068f694c8dc 100644 --- a/scene/2d/line_builder.h +++ b/scene/2d/line_builder.h @@ -37,7 +37,7 @@ class LineBuilder { public: // TODO Move in a struct and reference it // Input - LocalVector *points; + LocalVector points; Line2D::LineJointMode joint_mode = Line2D::LINE_JOINT_SHARP; Line2D::LineCapMode begin_cap_mode = Line2D::LINE_CAP_NONE; Line2D::LineCapMode end_cap_mode = Line2D::LINE_CAP_NONE; diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index 8926eb1d511a..17d506930c2a 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -476,6 +476,7 @@ void Curve::set_bake_resolution(int p_resolution) { ERR_FAIL_COND(p_resolution > 1000); _bake_resolution = p_resolution; _baked_cache_dirty = true; + emit_changed(); } real_t Curve::sample_baked(real_t p_offset) const { From 636c35204e1aa8ebb85b153b8b51ef80397341eb Mon Sep 17 00:00:00 2001 From: Ryan Chew Date: Sun, 18 Aug 2024 15:47:05 -0700 Subject: [PATCH 3/3] Change curve_line_segments property name to min_curve_line_segments --- doc/classes/Line2D.xml | 4 ++-- scene/2d/line_2d.cpp | 12 ++++++------ scene/2d/line_2d.h | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/doc/classes/Line2D.xml b/doc/classes/Line2D.xml index 768aa469a3fd..2b67d0e8669e 100644 --- a/doc/classes/Line2D.xml +++ b/doc/classes/Line2D.xml @@ -70,8 +70,8 @@ [b]Note:[/b] The shape of the closing segment is not guaranteed to be seamless if a [member width_curve] is provided. [b]Note:[/b] The joint between the closing segment and the first segment is drawn first and it samples the [member gradient] and the [member width_curve] at the beginning. This is an implementation detail that might change in a future version. - - Subdivide the line into the number of [member curve_line_segments] at minimum. The resulting segment count varies depending on the use of a fill gradient and the distances between points. + + Subdivide the line into the number of at least [member min_curve_line_segments] segments. The resulting segment count varies depending on the use of a fill gradient and the distances between points. [b]Note:[/b] Using higher values results in more segments (and vertices/indices) that need to be generated which can degrade performance, particularly if you are animating properties on your line. diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp index 014389062b8e..84042bcd77cb 100644 --- a/scene/2d/line_2d.cpp +++ b/scene/2d/line_2d.cpp @@ -119,8 +119,8 @@ Ref Line2D::get_curve() const { return _curve; } -void Line2D::set_curve_line_segments(int curve_line_segments) { - _curve_line_segments = curve_line_segments; +void Line2D::set_min_curve_line_segments(int min_curve_line_segments) { + _min_curve_line_segments = min_curve_line_segments; queue_redraw(); } @@ -352,7 +352,7 @@ void Line2D::_draw() { } int subline_count = static_cast(gradient_inclusive_points.size()) - 1; - int num_segments = _curve.is_valid() ? _curve_line_segments : -1; + int num_segments = _curve.is_valid() ? _min_curve_line_segments : -1; num_segments = MAX(subline_count, num_segments); generated_draw_points.reserve(num_segments); generated_draw_points.clear(); @@ -475,8 +475,8 @@ void Line2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_curve", "curve"), &Line2D::set_curve); ClassDB::bind_method(D_METHOD("get_curve"), &Line2D::get_curve); - ClassDB::bind_method(D_METHOD("set_curve_line_segments", "curve_offset"), &Line2D::set_curve_line_segments); - ClassDB::bind_method(D_METHOD("get_curve_line_segments"), &Line2D::get_curve_line_segments); + ClassDB::bind_method(D_METHOD("set_min_curve_line_segments", "curve_offset"), &Line2D::set_min_curve_line_segments); + ClassDB::bind_method(D_METHOD("get_min_curve_line_segments"), &Line2D::get_min_curve_line_segments); ClassDB::bind_method(D_METHOD("set_curve_offset", "curve_offset"), &Line2D::set_curve_offset); ClassDB::bind_method(D_METHOD("get_curve_offset"), &Line2D::get_curve_offset); @@ -515,7 +515,7 @@ void Line2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "closed"), "set_closed", "is_closed"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "width", PROPERTY_HINT_NONE, "suffix:px"), "set_width", "get_width"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "width_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_curve", "get_curve"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "curve_line_segments", PROPERTY_HINT_RANGE, "1,64,1,or_greater"), "set_curve_line_segments", "get_curve_line_segments"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "min_curve_line_segments", PROPERTY_HINT_RANGE, "1,64,1,or_greater"), "set_min_curve_line_segments", "get_min_curve_line_segments"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "curve_offset", PROPERTY_HINT_RANGE, "-1.0,1.0,0.01"), "set_curve_offset", "get_curve_offset"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "default_color"), "set_default_color", "get_default_color"); ADD_GROUP("Fill", ""); diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h index 6d6246f6485e..969c78e46da7 100644 --- a/scene/2d/line_2d.h +++ b/scene/2d/line_2d.h @@ -85,8 +85,8 @@ class Line2D : public Node2D { void set_curve(const Ref &curve); Ref get_curve() const; - void set_curve_line_segments(int curve_line_segments); - int get_curve_line_segments() const { return _curve_line_segments; } + void set_min_curve_line_segments(int min_curve_line_segments); + int get_min_curve_line_segments() const { return _min_curve_line_segments; } void set_curve_offset(float curve_offset); float get_curve_offset() const { return _curve_offset; } @@ -139,7 +139,7 @@ class Line2D : public Node2D { bool _closed = false; float _width = 10.0; Ref _curve; - int _curve_line_segments = 1; + int _min_curve_line_segments = 1; float _curve_offset = 0.0f; Color _default_color = Color(1, 1, 1); Ref _gradient;