Move rotation sampling from Path2D to Curve2D

The rotation sampling code is moved from Path2D to a new method
`sample_baked_with_rotation` on Curve2D.
This commit is contained in:
Yaohua Xiong 2022-08-05 17:03:00 +08:00
parent c29fe310f1
commit 399c2a8dea
4 changed files with 64 additions and 39 deletions

View File

@ -102,6 +102,22 @@
Cubic interpolation tends to follow the curves better, but linear is faster (and often, precise enough). Cubic interpolation tends to follow the curves better, but linear is faster (and often, precise enough).
</description> </description>
</method> </method>
<method name="sample_baked_with_rotation" qualifiers="const">
<return type="Transform2D" />
<param index="0" name="offset" type="float" />
<param index="1" name="cubic" type="bool" default="false" />
<param index="2" name="loop" type="bool" default="true" />
<param index="3" name="lookahead" type="float" default="4.0" />
<description>
Similar to [method sample_baked], but returns [Transform2D] that includes a rotation along the curve. Returns empty transform if length of the curve is [code]0[/code].
Use [param loop] to smooth the tangent at the end of the curve. [param lookahead] defines the distance to a nearby point for calculating the tangent vector.
[codeblock]
var transform = curve.sample_baked_with_rotation(offset)
position = transform.get_origin()
rotation = transform.get_rotation()
[/codeblock]
</description>
</method>
<method name="samplef" qualifiers="const"> <method name="samplef" qualifiers="const">
<return type="Vector2" /> <return type="Vector2" />
<param index="0" name="fofs" type="float" /> <param index="0" name="fofs" type="float" />

View File

@ -175,51 +175,18 @@ void PathFollow2D::_update_transform() {
if (path_length == 0) { if (path_length == 0) {
return; return;
} }
Vector2 pos = c->sample_baked(progress, cubic);
if (rotates) { if (rotates) {
real_t ahead = progress + lookahead; Transform2D xform = c->sample_baked_with_rotation(progress, cubic, loop, lookahead);
xform.translate_local(v_offset, h_offset);
if (loop && ahead >= path_length) { set_rotation(xform[1].angle());
// If our lookahead will loop, we need to check if the path is closed. set_position(xform[2]);
int point_count = c->get_point_count();
if (point_count > 0) {
Vector2 start_point = c->get_point_position(0);
Vector2 end_point = c->get_point_position(point_count - 1);
if (start_point == end_point) {
// Since the path is closed we want to 'smooth off'
// the corner at the start/end.
// So we wrap the lookahead back round.
ahead = Math::fmod(ahead, path_length);
}
}
}
Vector2 ahead_pos = c->sample_baked(ahead, cubic);
Vector2 tangent_to_curve;
if (ahead_pos == pos) {
// This will happen at the end of non-looping or non-closed paths.
// We'll try a look behind instead, in order to get a meaningful angle.
tangent_to_curve =
(pos - c->sample_baked(progress - lookahead, cubic)).normalized();
} else {
tangent_to_curve = (ahead_pos - pos).normalized();
}
Vector2 normal_of_curve = -tangent_to_curve.orthogonal();
pos += tangent_to_curve * h_offset;
pos += normal_of_curve * v_offset;
set_rotation(tangent_to_curve.angle());
} else { } else {
Vector2 pos = c->sample_baked(progress, cubic);
pos.x += h_offset; pos.x += h_offset;
pos.y += v_offset; pos.y += v_offset;
set_position(pos);
} }
set_position(pos);
} }
void PathFollow2D::_notification(int p_what) { void PathFollow2D::_notification(int p_what) {

View File

@ -936,6 +936,46 @@ Vector2 Curve2D::sample_baked(real_t p_offset, bool p_cubic) const {
} }
} }
Transform2D Curve2D::sample_baked_with_rotation(real_t p_offset, bool p_cubic, bool p_loop, real_t p_lookahead) const {
real_t path_length = get_baked_length(); // Ensure baked.
ERR_FAIL_COND_V_MSG(path_length == 0, Transform2D(), "Length of Curve2D is 0.");
Vector2 pos = sample_baked(p_offset, p_cubic);
real_t ahead = p_offset + p_lookahead;
if (p_loop && ahead >= path_length) {
// If our lookahead will loop, we need to check if the path is closed.
int point_count = get_point_count();
if (point_count > 0) {
Vector2 start_point = get_point_position(0);
Vector2 end_point = get_point_position(point_count - 1);
if (start_point == end_point) {
// Since the path is closed we want to 'smooth off'
// the corner at the start/end.
// So we wrap the lookahead back round.
ahead = Math::fmod(ahead, path_length);
}
}
}
Vector2 ahead_pos = sample_baked(ahead, p_cubic);
Vector2 tangent_to_curve;
if (ahead_pos == pos) {
// This will happen at the end of non-looping or non-closed paths.
// We'll try a look behind instead, in order to get a meaningful angle.
tangent_to_curve =
(pos - sample_baked(p_offset - p_lookahead, p_cubic)).normalized();
} else {
tangent_to_curve = (ahead_pos - pos).normalized();
}
Vector2 normal_of_curve = -tangent_to_curve.orthogonal();
return Transform2D(normal_of_curve, tangent_to_curve, pos);
}
PackedVector2Array Curve2D::get_baked_points() const { PackedVector2Array Curve2D::get_baked_points() const {
if (baked_cache_dirty) { if (baked_cache_dirty) {
_bake(); _bake();
@ -1184,6 +1224,7 @@ void Curve2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_baked_length"), &Curve2D::get_baked_length); ClassDB::bind_method(D_METHOD("get_baked_length"), &Curve2D::get_baked_length);
ClassDB::bind_method(D_METHOD("sample_baked", "offset", "cubic"), &Curve2D::sample_baked, DEFVAL(false)); ClassDB::bind_method(D_METHOD("sample_baked", "offset", "cubic"), &Curve2D::sample_baked, DEFVAL(false));
ClassDB::bind_method(D_METHOD("sample_baked_with_rotation", "offset", "cubic", "loop", "lookahead"), &Curve2D::sample_baked_with_rotation, DEFVAL(false), DEFVAL(true), DEFVAL(4.0));
ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve2D::get_baked_points); ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve2D::get_baked_points);
ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Curve2D::get_closest_point); ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Curve2D::get_closest_point);
ClassDB::bind_method(D_METHOD("get_closest_offset", "to_point"), &Curve2D::get_closest_offset); ClassDB::bind_method(D_METHOD("get_closest_offset", "to_point"), &Curve2D::get_closest_offset);

View File

@ -216,6 +216,7 @@ public:
real_t get_baked_length() const; real_t get_baked_length() const;
Vector2 sample_baked(real_t p_offset, bool p_cubic = false) const; Vector2 sample_baked(real_t p_offset, bool p_cubic = false) const;
Transform2D sample_baked_with_rotation(real_t p_offset, bool p_cubic = false, bool p_loop = true, real_t p_lookahead = 4.0) const;
PackedVector2Array get_baked_points() const; //useful for going through PackedVector2Array get_baked_points() const; //useful for going through
Vector2 get_closest_point(const Vector2 &p_to_point) const; Vector2 get_closest_point(const Vector2 &p_to_point) const;
real_t get_closest_offset(const Vector2 &p_to_point) const; real_t get_closest_offset(const Vector2 &p_to_point) const;