You've already forked godot
mirror of
https://github.com/godotengine/godot.git
synced 2025-11-04 12:00:25 +00:00
Extend Curve to allow for arbitrary domains
This commit is contained in:
@@ -70,14 +70,14 @@
|
||||
If [code]true[/code], breaks the loop at the end of the loop cycle for transition, even if the animation is looping.
|
||||
</member>
|
||||
<member name="fadein_curve" type="Curve" setter="set_fadein_curve" getter="get_fadein_curve">
|
||||
Determines how cross-fading between animations is eased. If empty, the transition will be linear.
|
||||
Determines how cross-fading between animations is eased. If empty, the transition will be linear. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="fadein_time" type="float" setter="set_fadein_time" getter="get_fadein_time" default="0.0">
|
||||
The fade-in duration. For example, setting this to [code]1.0[/code] for a 5 second length animation will produce a cross-fade that starts at 0 second and ends at 1 second during the animation.
|
||||
[b]Note:[/b] [AnimationNodeOneShot] transitions the current state after the end of the fading. When [AnimationNodeOutput] is considered as the most upstream, so the [member fadein_time] is scaled depending on the downstream delta. For example, if this value is set to [code]1.0[/code] and a [AnimationNodeTimeScale] with a value of [code]2.0[/code] is chained downstream, the actual processing time will be 0.5 second.
|
||||
</member>
|
||||
<member name="fadeout_curve" type="Curve" setter="set_fadeout_curve" getter="get_fadeout_curve">
|
||||
Determines how cross-fading between animations is eased. If empty, the transition will be linear.
|
||||
Determines how cross-fading between animations is eased. If empty, the transition will be linear. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="fadeout_time" type="float" setter="set_fadeout_time" getter="get_fadeout_time" default="0.0">
|
||||
The fade-out duration. For example, setting this to [code]1.0[/code] for a 5 second length animation will produce a cross-fade that starts at 4 second and ends at 5 second during the animation.
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
The transition type.
|
||||
</member>
|
||||
<member name="xfade_curve" type="Curve" setter="set_xfade_curve" getter="get_xfade_curve">
|
||||
Ease curve for better control over cross-fade between this state and the next.
|
||||
Ease curve for better control over cross-fade between this state and the next. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="xfade_time" type="float" setter="set_xfade_time" getter="get_xfade_time" default="0.0">
|
||||
The time to cross-fade between this state and the next.
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
The number of enabled input ports for this animation node.
|
||||
</member>
|
||||
<member name="xfade_curve" type="Curve" setter="set_xfade_curve" getter="get_xfade_curve">
|
||||
Determines how cross-fading between animations is eased. If empty, the transition will be linear.
|
||||
Determines how cross-fading between animations is eased. If empty, the transition will be linear. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="xfade_time" type="float" setter="set_xfade_time" getter="get_xfade_time" default="0.0">
|
||||
Cross-fading time (in seconds) between each animation connected to the inputs.
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
<param index="0" name="param" type="int" enum="CPUParticles2D.Parameter" />
|
||||
<param index="1" name="curve" type="Curve" />
|
||||
<description>
|
||||
Sets the [Curve] of the parameter specified by [enum Parameter].
|
||||
Sets the [Curve] of the parameter specified by [enum Parameter]. Should be a unit [Curve].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_param_max">
|
||||
@@ -90,7 +90,7 @@
|
||||
Number of particles emitted in one emission cycle.
|
||||
</member>
|
||||
<member name="angle_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's rotation will be animated along this [Curve].
|
||||
Each particle's rotation will be animated along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="angle_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
|
||||
Maximum initial rotation applied to each particle, in degrees.
|
||||
@@ -99,7 +99,7 @@
|
||||
Minimum equivalent of [member angle_max].
|
||||
</member>
|
||||
<member name="angular_velocity_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's angular velocity will vary along this [Curve].
|
||||
Each particle's angular velocity will vary along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="angular_velocity_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
|
||||
Maximum initial angular velocity (rotation speed) applied to each particle in [i]degrees[/i] per second.
|
||||
@@ -108,7 +108,7 @@
|
||||
Minimum equivalent of [member angular_velocity_max].
|
||||
</member>
|
||||
<member name="anim_offset_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's animation offset will vary along this [Curve].
|
||||
Each particle's animation offset will vary along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="anim_offset_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
|
||||
Maximum animation offset that corresponds to frame index in the texture. [code]0[/code] is the first frame, [code]1[/code] is the last one. See [member CanvasItemMaterial.particles_animation].
|
||||
@@ -117,7 +117,7 @@
|
||||
Minimum equivalent of [member anim_offset_max].
|
||||
</member>
|
||||
<member name="anim_speed_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's animation speed will vary along this [Curve].
|
||||
Each particle's animation speed will vary along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="anim_speed_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
|
||||
Maximum particle animation speed. Animation speed of [code]1[/code] means that the particles will make full [code]0[/code] to [code]1[/code] offset cycle during lifetime, [code]2[/code] means [code]2[/code] cycles etc.
|
||||
@@ -136,7 +136,7 @@
|
||||
Each particle's color will vary along this [Gradient] (multiplied with [member color]).
|
||||
</member>
|
||||
<member name="damping_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Damping will vary along this [Curve].
|
||||
Damping will vary along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="damping_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
|
||||
The maximum rate at which particles lose velocity. For example value of [code]100[/code] means that the particle will go from [code]100[/code] velocity to [code]0[/code] in [code]1[/code] second.
|
||||
@@ -184,7 +184,7 @@
|
||||
Gravity applied to every particle.
|
||||
</member>
|
||||
<member name="hue_variation_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's hue will vary along this [Curve].
|
||||
Each particle's hue will vary along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="hue_variation_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
|
||||
Maximum initial hue variation applied to each particle. It will shift the particle color's hue.
|
||||
@@ -205,7 +205,7 @@
|
||||
Particle lifetime randomness ratio.
|
||||
</member>
|
||||
<member name="linear_accel_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's linear acceleration will vary along this [Curve].
|
||||
Each particle's linear acceleration will vary along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="linear_accel_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
|
||||
Maximum linear acceleration applied to each particle in the direction of motion.
|
||||
@@ -220,7 +220,7 @@
|
||||
If [code]true[/code], only one emission cycle occurs. If set [code]true[/code] during a cycle, emission will stop at the cycle's end.
|
||||
</member>
|
||||
<member name="orbit_velocity_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's orbital velocity will vary along this [Curve].
|
||||
Each particle's orbital velocity will vary along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="orbit_velocity_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
|
||||
Maximum orbital velocity applied to each particle. Makes the particles circle around origin. Specified in number of full rotations around origin per second.
|
||||
@@ -235,7 +235,7 @@
|
||||
Particle system starts as if it had already run for this many seconds.
|
||||
</member>
|
||||
<member name="radial_accel_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's radial acceleration will vary along this [Curve].
|
||||
Each particle's radial acceleration will vary along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="radial_accel_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
|
||||
Maximum radial acceleration applied to each particle. Makes particle accelerate away from the origin or towards it if negative.
|
||||
@@ -247,7 +247,7 @@
|
||||
Emission lifetime randomness ratio.
|
||||
</member>
|
||||
<member name="scale_amount_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's scale will vary along this [Curve].
|
||||
Each particle's scale will vary along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="scale_amount_max" type="float" setter="set_param_max" getter="get_param_max" default="1.0">
|
||||
Maximum initial scale applied to each particle.
|
||||
@@ -256,11 +256,11 @@
|
||||
Minimum equivalent of [member scale_amount_max].
|
||||
</member>
|
||||
<member name="scale_curve_x" type="Curve" setter="set_scale_curve_x" getter="get_scale_curve_x">
|
||||
Each particle's horizontal scale will vary along this [Curve].
|
||||
Each particle's horizontal scale will vary along this [Curve]. Should be a unit [Curve].
|
||||
[member split_scale] must be enabled.
|
||||
</member>
|
||||
<member name="scale_curve_y" type="Curve" setter="set_scale_curve_y" getter="get_scale_curve_y">
|
||||
Each particle's vertical scale will vary along this [Curve].
|
||||
Each particle's vertical scale will vary along this [Curve]. Should be a unit [Curve].
|
||||
[member split_scale] must be enabled.
|
||||
</member>
|
||||
<member name="speed_scale" type="float" setter="set_speed_scale" getter="get_speed_scale" default="1.0">
|
||||
@@ -273,7 +273,7 @@
|
||||
Each particle's initial direction range from [code]+spread[/code] to [code]-spread[/code] degrees.
|
||||
</member>
|
||||
<member name="tangential_accel_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's tangential acceleration will vary along this [Curve].
|
||||
Each particle's tangential acceleration will vary along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="tangential_accel_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
|
||||
Maximum tangential acceleration applied to each particle. Tangential acceleration is perpendicular to the particle's velocity giving the particles a swirling motion.
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
<param index="0" name="param" type="int" enum="CPUParticles3D.Parameter" />
|
||||
<param index="1" name="curve" type="Curve" />
|
||||
<description>
|
||||
Sets the [Curve] of the parameter specified by [enum Parameter].
|
||||
Sets the [Curve] of the parameter specified by [enum Parameter]. Should be a unit [Curve].
|
||||
</description>
|
||||
</method>
|
||||
<method name="set_param_max">
|
||||
@@ -96,7 +96,7 @@
|
||||
Number of particles emitted in one emission cycle.
|
||||
</member>
|
||||
<member name="angle_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's rotation will be animated along this [Curve].
|
||||
Each particle's rotation will be animated along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="angle_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
|
||||
Maximum angle.
|
||||
@@ -105,7 +105,7 @@
|
||||
Minimum angle.
|
||||
</member>
|
||||
<member name="angular_velocity_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's angular velocity (rotation speed) will vary along this [Curve] over its lifetime.
|
||||
Each particle's angular velocity (rotation speed) will vary along this [Curve] over its lifetime. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="angular_velocity_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
|
||||
Maximum initial angular velocity (rotation speed) applied to each particle in [i]degrees[/i] per second.
|
||||
@@ -114,7 +114,7 @@
|
||||
Minimum initial angular velocity (rotation speed) applied to each particle in [i]degrees[/i] per second.
|
||||
</member>
|
||||
<member name="anim_offset_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's animation offset will vary along this [Curve].
|
||||
Each particle's animation offset will vary along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="anim_offset_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
|
||||
Maximum animation offset.
|
||||
@@ -123,7 +123,7 @@
|
||||
Minimum animation offset.
|
||||
</member>
|
||||
<member name="anim_speed_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's animation speed will vary along this [Curve].
|
||||
Each particle's animation speed will vary along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="anim_speed_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
|
||||
Maximum particle animation speed.
|
||||
@@ -144,7 +144,7 @@
|
||||
[b]Note:[/b] [member color_ramp] multiplies the particle mesh's vertex colors. To have a visible effect on a [BaseMaterial3D], [member BaseMaterial3D.vertex_color_use_as_albedo] [i]must[/i] be [code]true[/code]. For a [ShaderMaterial], [code]ALBEDO *= COLOR.rgb;[/code] must be inserted in the shader's [code]fragment()[/code] function. Otherwise, [member color_ramp] will have no visible effect.
|
||||
</member>
|
||||
<member name="damping_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Damping will vary along this [Curve].
|
||||
Damping will vary along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="damping_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
|
||||
Maximum damping.
|
||||
@@ -212,7 +212,7 @@
|
||||
Gravity applied to every particle.
|
||||
</member>
|
||||
<member name="hue_variation_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's hue will vary along this [Curve].
|
||||
Each particle's hue will vary along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="hue_variation_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
|
||||
Maximum hue variation.
|
||||
@@ -233,7 +233,7 @@
|
||||
Particle lifetime randomness ratio.
|
||||
</member>
|
||||
<member name="linear_accel_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's linear acceleration will vary along this [Curve].
|
||||
Each particle's linear acceleration will vary along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="linear_accel_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
|
||||
Maximum linear acceleration.
|
||||
@@ -251,7 +251,7 @@
|
||||
If [code]true[/code], only one emission cycle occurs. If set [code]true[/code] during a cycle, emission will stop at the cycle's end.
|
||||
</member>
|
||||
<member name="orbit_velocity_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's orbital velocity will vary along this [Curve].
|
||||
Each particle's orbital velocity will vary along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="orbit_velocity_max" type="float" setter="set_param_max" getter="get_param_max">
|
||||
Maximum orbit velocity.
|
||||
@@ -272,7 +272,7 @@
|
||||
Particle system starts as if it had already run for this many seconds.
|
||||
</member>
|
||||
<member name="radial_accel_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's radial acceleration will vary along this [Curve].
|
||||
Each particle's radial acceleration will vary along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="radial_accel_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
|
||||
Maximum radial acceleration.
|
||||
@@ -284,7 +284,7 @@
|
||||
Emission lifetime randomness ratio.
|
||||
</member>
|
||||
<member name="scale_amount_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's scale will vary along this [Curve].
|
||||
Each particle's scale will vary along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="scale_amount_max" type="float" setter="set_param_max" getter="get_param_max" default="1.0">
|
||||
Maximum scale.
|
||||
@@ -311,7 +311,7 @@
|
||||
Each particle's initial direction range from [code]+spread[/code] to [code]-spread[/code] degrees. Applied to X/Z plane and Y/Z planes.
|
||||
</member>
|
||||
<member name="tangential_accel_curve" type="Curve" setter="set_param_curve" getter="get_param_curve">
|
||||
Each particle's tangential acceleration will vary along this [Curve].
|
||||
Each particle's tangential acceleration will vary along this [Curve]. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="tangential_accel_max" type="float" setter="set_param_max" getter="get_param_max" default="0.0">
|
||||
Maximum tangent acceleration.
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
A mathematical curve.
|
||||
</brief_description>
|
||||
<description>
|
||||
This resource describes a mathematical curve by defining a set of points and tangents at each point. By default, it ranges between [code]0[/code] and [code]1[/code] on the Y axis and positions points relative to the [code]0.5[/code] Y position.
|
||||
See also [Gradient] which is designed for color interpolation. See also [Curve2D] and [Curve3D].
|
||||
This resource describes a mathematical curve by defining a set of points and tangents at each point. By default, it ranges between [code]0[/code] and [code]1[/code] on the X and Y axes, but these ranges can be changed.
|
||||
Please note that many resources and nodes assume they are given [i]unit curves[/i]. A unit curve is a curve whose domain (the X axis) is between [code]0[/code] and [code]1[/code]. Some examples of unit curve usage are [member CPUParticles2D.angle_curve] and [member Line2D.width_curve].
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
@@ -39,6 +39,12 @@
|
||||
Removes all points from the curve.
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_domain_range" qualifiers="const">
|
||||
<return type="float" />
|
||||
<description>
|
||||
Returns the difference between [member min_domain] and [member max_domain].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_point_left_mode" qualifiers="const">
|
||||
<return type="int" enum="Curve.TangentMode" />
|
||||
<param index="0" name="index" type="int" />
|
||||
@@ -74,6 +80,12 @@
|
||||
Returns the right tangent angle (in degrees) for the point at [param index].
|
||||
</description>
|
||||
</method>
|
||||
<method name="get_value_range" qualifiers="const">
|
||||
<return type="float" />
|
||||
<description>
|
||||
Returns the difference between [member min_value] and [member max_value].
|
||||
</description>
|
||||
</method>
|
||||
<method name="remove_point">
|
||||
<return type="void" />
|
||||
<param index="0" name="index" type="int" />
|
||||
@@ -148,17 +160,28 @@
|
||||
<member name="bake_resolution" type="int" setter="set_bake_resolution" getter="get_bake_resolution" default="100">
|
||||
The number of points to include in the baked (i.e. cached) curve data.
|
||||
</member>
|
||||
<member name="max_domain" type="float" setter="set_max_domain" getter="get_max_domain" default="1.0">
|
||||
The maximum domain (x-coordinate) that points can have.
|
||||
</member>
|
||||
<member name="max_value" type="float" setter="set_max_value" getter="get_max_value" default="1.0">
|
||||
The maximum value the curve can reach.
|
||||
The maximum value (y-coordinate) that points can have. Tangents can cause higher values between points.
|
||||
</member>
|
||||
<member name="min_domain" type="float" setter="set_min_domain" getter="get_min_domain" default="0.0">
|
||||
The minimum domain (x-coordinate) that points can have.
|
||||
</member>
|
||||
<member name="min_value" type="float" setter="set_min_value" getter="get_min_value" default="0.0">
|
||||
The minimum value the curve can reach.
|
||||
The minimum value (y-coordinate) that points can have. Tangents can cause lower values between points.
|
||||
</member>
|
||||
<member name="point_count" type="int" setter="set_point_count" getter="get_point_count" default="0">
|
||||
The number of points describing the curve.
|
||||
</member>
|
||||
</members>
|
||||
<signals>
|
||||
<signal name="domain_changed">
|
||||
<description>
|
||||
Emitted when [member max_domain] or [member min_domain] is changed.
|
||||
</description>
|
||||
</signal>
|
||||
<signal name="range_changed">
|
||||
<description>
|
||||
Emitted when [member max_value] or [member min_value] is changed.
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
A 1D texture where pixel brightness corresponds to points on a curve.
|
||||
</brief_description>
|
||||
<description>
|
||||
A 1D texture where pixel brightness corresponds to points on a [Curve] resource, either in grayscale or in red. This visual representation simplifies the task of saving curves as image files.
|
||||
A 1D texture where pixel brightness corresponds to points on a unit [Curve] resource, either in grayscale or in red. This visual representation simplifies the task of saving curves as image files.
|
||||
If you need to store up to 3 curves within a single texture, use [CurveXYZTexture] instead. See also [GradientTexture1D] and [GradientTexture2D].
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<members>
|
||||
<member name="curve" type="Curve" setter="set_curve" getter="get_curve">
|
||||
The [Curve] that is rendered onto the texture.
|
||||
The [Curve] that is rendered onto the texture. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="resource_local_to_scene" type="bool" setter="set_local_to_scene" getter="is_local_to_scene" overrides="Resource" default="false" />
|
||||
<member name="texture_mode" type="int" setter="set_texture_mode" getter="get_texture_mode" enum="CurveTexture.TextureMode" default="0">
|
||||
|
||||
@@ -4,20 +4,20 @@
|
||||
A 1D texture where the red, green, and blue color channels correspond to points on 3 curves.
|
||||
</brief_description>
|
||||
<description>
|
||||
A 1D texture where the red, green, and blue color channels correspond to points on 3 [Curve] resources. Compared to using separate [CurveTexture]s, this further simplifies the task of saving curves as image files.
|
||||
A 1D texture where the red, green, and blue color channels correspond to points on 3 unit [Curve] resources. Compared to using separate [CurveTexture]s, this further simplifies the task of saving curves as image files.
|
||||
If you only need to store one curve within a single texture, use [CurveTexture] instead. See also [GradientTexture1D] and [GradientTexture2D].
|
||||
</description>
|
||||
<tutorials>
|
||||
</tutorials>
|
||||
<members>
|
||||
<member name="curve_x" type="Curve" setter="set_curve_x" getter="get_curve_x">
|
||||
The [Curve] that is rendered onto the texture's red channel.
|
||||
The [Curve] that is rendered onto the texture's red channel. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="curve_y" type="Curve" setter="set_curve_y" getter="get_curve_y">
|
||||
The [Curve] that is rendered onto the texture's green channel.
|
||||
The [Curve] that is rendered onto the texture's green channel. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="curve_z" type="Curve" setter="set_curve_z" getter="get_curve_z">
|
||||
The [Curve] that is rendered onto the texture's blue channel.
|
||||
The [Curve] that is rendered onto the texture's blue channel. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="resource_local_to_scene" type="bool" setter="set_local_to_scene" getter="is_local_to_scene" overrides="Resource" default="false" />
|
||||
<member name="width" type="int" setter="set_width" getter="get_width" default="256">
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
The polyline's width.
|
||||
</member>
|
||||
<member name="width_curve" type="Curve" setter="set_curve" getter="get_curve">
|
||||
The polyline's width curve. The width of the polyline over its length will be equivalent to the value of the width curve over its domain.
|
||||
The polyline's width curve. The width of the polyline over its length will be equivalent to the value of the width curve over its domain. The width curve should be a unit [Curve].
|
||||
</member>
|
||||
</members>
|
||||
<constants>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
</tutorials>
|
||||
<members>
|
||||
<member name="curve" type="Curve" setter="set_curve" getter="get_curve">
|
||||
Determines the size of the ribbon along its length. The size of a particular section segment is obtained by multiplying the baseline [member size] by the value of this curve at the given distance. For values smaller than [code]0[/code], the faces will be inverted.
|
||||
Determines the size of the ribbon along its length. The size of a particular section segment is obtained by multiplying the baseline [member size] by the value of this curve at the given distance. For values smaller than [code]0[/code], the faces will be inverted. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="section_length" type="float" setter="set_section_length" getter="get_section_length" default="0.2">
|
||||
The length of a section of the ribbon.
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
If [code]true[/code], generates a cap at the top of the tube. This can be set to [code]false[/code] to speed up generation and rendering when the cap is never seen by the camera.
|
||||
</member>
|
||||
<member name="curve" type="Curve" setter="set_curve" getter="get_curve">
|
||||
Determines the radius of the tube along its length. The radius of a particular section ring is obtained by multiplying the baseline [member radius] by the value of this curve at the given distance. For values smaller than [code]0[/code], the faces will be inverted.
|
||||
Determines the radius of the tube along its length. The radius of a particular section ring is obtained by multiplying the baseline [member radius] by the value of this curve at the given distance. For values smaller than [code]0[/code], the faces will be inverted. Should be a unit [Curve].
|
||||
</member>
|
||||
<member name="radial_steps" type="int" setter="set_radial_steps" getter="get_radial_steps" default="8">
|
||||
The number of sides on the tube. For example, a value of [code]5[/code] means the tube will be pentagonal. Higher values result in a more detailed tube at the cost of performance.
|
||||
|
||||
@@ -64,6 +64,7 @@ void CurveEdit::set_curve(Ref<Curve> p_curve) {
|
||||
if (curve.is_valid()) {
|
||||
curve->disconnect_changed(callable_mp(this, &CurveEdit::_curve_changed));
|
||||
curve->disconnect(Curve::SIGNAL_RANGE_CHANGED, callable_mp(this, &CurveEdit::_curve_changed));
|
||||
curve->disconnect(Curve::SIGNAL_DOMAIN_CHANGED, callable_mp(this, &CurveEdit::_curve_changed));
|
||||
}
|
||||
|
||||
curve = p_curve;
|
||||
@@ -71,6 +72,7 @@ void CurveEdit::set_curve(Ref<Curve> p_curve) {
|
||||
if (curve.is_valid()) {
|
||||
curve->connect_changed(callable_mp(this, &CurveEdit::_curve_changed));
|
||||
curve->connect(Curve::SIGNAL_RANGE_CHANGED, callable_mp(this, &CurveEdit::_curve_changed));
|
||||
curve->connect(Curve::SIGNAL_DOMAIN_CHANGED, callable_mp(this, &CurveEdit::_curve_changed));
|
||||
}
|
||||
|
||||
// Note: if you edit a curve, then set another, and try to undo,
|
||||
@@ -226,10 +228,10 @@ void CurveEdit::gui_input(const Ref<InputEvent> &p_event) {
|
||||
}
|
||||
} else if (grabbing == GRAB_NONE) {
|
||||
// Adding a new point. Insert a temporary point for the user to adjust, so it's not in the undo/redo.
|
||||
Vector2 new_pos = get_world_pos(mpos).clamp(Vector2(0.0, curve->get_min_value()), Vector2(1.0, curve->get_max_value()));
|
||||
Vector2 new_pos = get_world_pos(mpos).clamp(Vector2(curve->get_min_domain(), curve->get_min_value()), Vector2(curve->get_max_domain(), curve->get_max_value()));
|
||||
if (snap_enabled || mb->is_command_or_control_pressed()) {
|
||||
new_pos.x = Math::snapped(new_pos.x, 1.0 / snap_count);
|
||||
new_pos.y = Math::snapped(new_pos.y - curve->get_min_value(), curve->get_range() / snap_count) + curve->get_min_value();
|
||||
new_pos.x = Math::snapped(new_pos.x - curve->get_min_domain(), curve->get_domain_range() / snap_count) + curve->get_min_domain();
|
||||
new_pos.y = Math::snapped(new_pos.y - curve->get_min_value(), curve->get_value_range() / snap_count) + curve->get_min_value();
|
||||
}
|
||||
|
||||
new_pos.x = get_offset_without_collision(selected_index, new_pos.x, mpos.x >= get_view_pos(new_pos).x);
|
||||
@@ -276,11 +278,11 @@ void CurveEdit::gui_input(const Ref<InputEvent> &p_event) {
|
||||
if (selected_index != -1) {
|
||||
if (selected_tangent_index == TANGENT_NONE) {
|
||||
// Drag point.
|
||||
Vector2 new_pos = get_world_pos(mpos).clamp(Vector2(0.0, curve->get_min_value()), Vector2(1.0, curve->get_max_value()));
|
||||
Vector2 new_pos = get_world_pos(mpos).clamp(Vector2(curve->get_min_domain(), curve->get_min_value()), Vector2(curve->get_max_domain(), curve->get_max_value()));
|
||||
|
||||
if (snap_enabled || mm->is_command_or_control_pressed()) {
|
||||
new_pos.x = Math::snapped(new_pos.x, 1.0 / snap_count);
|
||||
new_pos.y = Math::snapped(new_pos.y - curve->get_min_value(), curve->get_range() / snap_count) + curve->get_min_value();
|
||||
new_pos.x = Math::snapped(new_pos.x - curve->get_min_domain(), curve->get_domain_range() / snap_count) + curve->get_min_domain();
|
||||
new_pos.y = Math::snapped(new_pos.y - curve->get_min_value(), curve->get_value_range() / snap_count) + curve->get_min_value();
|
||||
}
|
||||
|
||||
// Allow to snap to axes with Shift.
|
||||
@@ -295,8 +297,8 @@ void CurveEdit::gui_input(const Ref<InputEvent> &p_event) {
|
||||
|
||||
// Allow to constraint the point between the adjacent two with Alt.
|
||||
if (mm->is_alt_pressed()) {
|
||||
float prev_point_offset = (selected_index > 0) ? (curve->get_point_position(selected_index - 1).x + 0.00001) : 0.0;
|
||||
float next_point_offset = (selected_index < curve->get_point_count() - 1) ? (curve->get_point_position(selected_index + 1).x - 0.00001) : 1.0;
|
||||
float prev_point_offset = (selected_index > 0) ? (curve->get_point_position(selected_index - 1).x + 0.00001) : curve->get_min_domain();
|
||||
float next_point_offset = (selected_index < curve->get_point_count() - 1) ? (curve->get_point_position(selected_index + 1).x - 0.00001) : curve->get_max_domain();
|
||||
new_pos.x = CLAMP(new_pos.x, prev_point_offset, next_point_offset);
|
||||
}
|
||||
|
||||
@@ -357,37 +359,39 @@ void CurveEdit::use_preset(int p_preset_id) {
|
||||
Array previous_data = curve->get_data();
|
||||
curve->clear_points();
|
||||
|
||||
float min_value = curve->get_min_value();
|
||||
float max_value = curve->get_max_value();
|
||||
const float min_y = curve->get_min_value();
|
||||
const float max_y = curve->get_max_value();
|
||||
const float min_x = curve->get_min_domain();
|
||||
const float max_x = curve->get_max_domain();
|
||||
|
||||
switch (p_preset_id) {
|
||||
case PRESET_CONSTANT:
|
||||
curve->add_point(Vector2(0, (min_value + max_value) / 2.0));
|
||||
curve->add_point(Vector2(1, (min_value + max_value) / 2.0));
|
||||
curve->add_point(Vector2(min_x, (min_y + max_y) / 2.0));
|
||||
curve->add_point(Vector2(max_x, (min_y + max_y) / 2.0));
|
||||
curve->set_point_right_mode(0, Curve::TANGENT_LINEAR);
|
||||
curve->set_point_left_mode(1, Curve::TANGENT_LINEAR);
|
||||
break;
|
||||
|
||||
case PRESET_LINEAR:
|
||||
curve->add_point(Vector2(0, min_value));
|
||||
curve->add_point(Vector2(1, max_value));
|
||||
curve->add_point(Vector2(min_x, min_y));
|
||||
curve->add_point(Vector2(max_x, max_y));
|
||||
curve->set_point_right_mode(0, Curve::TANGENT_LINEAR);
|
||||
curve->set_point_left_mode(1, Curve::TANGENT_LINEAR);
|
||||
break;
|
||||
|
||||
case PRESET_EASE_IN:
|
||||
curve->add_point(Vector2(0, min_value));
|
||||
curve->add_point(Vector2(1, max_value), curve->get_range() * 1.4, 0);
|
||||
curve->add_point(Vector2(min_x, min_y));
|
||||
curve->add_point(Vector2(max_x, max_y), curve->get_value_range() / curve->get_domain_range() * 1.4, 0);
|
||||
break;
|
||||
|
||||
case PRESET_EASE_OUT:
|
||||
curve->add_point(Vector2(0, min_value), 0, curve->get_range() * 1.4);
|
||||
curve->add_point(Vector2(1, max_value));
|
||||
curve->add_point(Vector2(min_x, min_y), 0, curve->get_value_range() / curve->get_domain_range() * 1.4);
|
||||
curve->add_point(Vector2(max_x, max_y));
|
||||
break;
|
||||
|
||||
case PRESET_SMOOTHSTEP:
|
||||
curve->add_point(Vector2(0, min_value));
|
||||
curve->add_point(Vector2(1, max_value));
|
||||
curve->add_point(Vector2(min_x, min_y));
|
||||
curve->add_point(Vector2(max_x, max_y));
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -411,7 +415,7 @@ void CurveEdit::_curve_changed() {
|
||||
}
|
||||
}
|
||||
|
||||
int CurveEdit::get_point_at(Vector2 p_pos) const {
|
||||
int CurveEdit::get_point_at(const Vector2 &p_pos) const {
|
||||
if (curve.is_null()) {
|
||||
return -1;
|
||||
}
|
||||
@@ -432,7 +436,7 @@ int CurveEdit::get_point_at(Vector2 p_pos) const {
|
||||
return closest_idx;
|
||||
}
|
||||
|
||||
CurveEdit::TangentIndex CurveEdit::get_tangent_at(Vector2 p_pos) const {
|
||||
CurveEdit::TangentIndex CurveEdit::get_tangent_at(const Vector2 &p_pos) const {
|
||||
if (curve.is_null() || selected_index < 0) {
|
||||
return TANGENT_NONE;
|
||||
}
|
||||
@@ -491,7 +495,7 @@ float CurveEdit::get_offset_without_collision(int p_current_index, float p_offse
|
||||
return safe_offset;
|
||||
}
|
||||
|
||||
void CurveEdit::add_point(Vector2 p_pos) {
|
||||
void CurveEdit::add_point(const Vector2 &p_pos) {
|
||||
ERR_FAIL_COND(curve.is_null());
|
||||
|
||||
// Add a point to get its index, then remove it immediately. Trick to feed the UndoRedo.
|
||||
@@ -531,7 +535,7 @@ void CurveEdit::remove_point(int p_index) {
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
void CurveEdit::set_point_position(int p_index, Vector2 p_pos) {
|
||||
void CurveEdit::set_point_position(int p_index, const Vector2 &p_pos) {
|
||||
ERR_FAIL_COND(curve.is_null());
|
||||
ERR_FAIL_INDEX_MSG(p_index, curve->get_point_count(), "Curve point is out of bounds.");
|
||||
|
||||
@@ -657,10 +661,12 @@ void CurveEdit::update_view_transform() {
|
||||
|
||||
const real_t margin = font->get_height(font_size) + 2 * EDSCALE;
|
||||
|
||||
float min_x = curve.is_valid() ? curve->get_min_domain() : 0.0;
|
||||
float max_x = curve.is_valid() ? curve->get_max_domain() : 1.0;
|
||||
float min_y = curve.is_valid() ? curve->get_min_value() : 0.0;
|
||||
float max_y = curve.is_valid() ? curve->get_max_value() : 1.0;
|
||||
|
||||
const Rect2 world_rect = Rect2(Curve::MIN_X, min_y, Curve::MAX_X, max_y - min_y);
|
||||
const Rect2 world_rect = Rect2(min_x, min_y, max_x - min_x, max_y - min_y);
|
||||
const Size2 view_margin(margin, margin);
|
||||
const Size2 view_size = get_size() - view_margin * 2;
|
||||
const Vector2 scale = view_size / world_rect.size;
|
||||
@@ -707,71 +713,57 @@ Vector2 CurveEdit::get_tangent_view_pos(int p_index, TangentIndex p_tangent) con
|
||||
return tangent_view_pos;
|
||||
}
|
||||
|
||||
Vector2 CurveEdit::get_view_pos(Vector2 p_world_pos) const {
|
||||
Vector2 CurveEdit::get_view_pos(const Vector2 &p_world_pos) const {
|
||||
return _world_to_view.xform(p_world_pos);
|
||||
}
|
||||
|
||||
Vector2 CurveEdit::get_world_pos(Vector2 p_view_pos) const {
|
||||
Vector2 CurveEdit::get_world_pos(const Vector2 &p_view_pos) const {
|
||||
return _world_to_view.affine_inverse().xform(p_view_pos);
|
||||
}
|
||||
|
||||
// Uses non-baked points, but takes advantage of ordered iteration to be faster.
|
||||
template <typename T>
|
||||
static void plot_curve_accurate(const Curve &curve, float step, Vector2 scaling, T plot_func) {
|
||||
if (curve.get_point_count() <= 1) {
|
||||
// Not enough points to make a curve, so it's just a straight line.
|
||||
// The added tiny vectors make the drawn line stay exactly within the bounds in practice.
|
||||
float y = curve.sample(0);
|
||||
plot_func(Vector2(0, y) * scaling + Vector2(0.5, 0), Vector2(1.f, y) * scaling - Vector2(1.5, 0), true);
|
||||
void CurveEdit::plot_curve_accurate(float p_step, const Color &p_line_color, const Color &p_edge_line_color) {
|
||||
const real_t min_x = curve->get_min_domain();
|
||||
const real_t max_x = curve->get_max_domain();
|
||||
if (curve->get_point_count() <= 1) { // Draw single line through entire plot.
|
||||
real_t y = curve->sample(0);
|
||||
draw_line(get_view_pos(Vector2(min_x, y)) + Vector2(0.5, 0), get_view_pos(Vector2(max_x, y)) - Vector2(1.5, 0), p_line_color, LINE_WIDTH, true);
|
||||
return;
|
||||
}
|
||||
|
||||
} else {
|
||||
Vector2 first_point = curve.get_point_position(0);
|
||||
Vector2 last_point = curve.get_point_position(curve.get_point_count() - 1);
|
||||
Vector2 first_point = curve->get_point_position(0);
|
||||
Vector2 last_point = curve->get_point_position(curve->get_point_count() - 1);
|
||||
|
||||
// Edge lines
|
||||
plot_func(Vector2(0, first_point.y) * scaling + Vector2(0.5, 0), first_point * scaling, false);
|
||||
plot_func(Vector2(Curve::MAX_X, last_point.y) * scaling - Vector2(1.5, 0), last_point * scaling, false);
|
||||
// Transform pixels-per-step into curve domain. Only works for non-rotated transforms.
|
||||
const float world_step_size = p_step / _world_to_view.get_scale().x;
|
||||
|
||||
// Draw section by section, so that we get maximum precision near points.
|
||||
// It's an accurate representation, but slower than using the baked one.
|
||||
for (int i = 1; i < curve.get_point_count(); ++i) {
|
||||
Vector2 a = curve.get_point_position(i - 1);
|
||||
Vector2 b = curve.get_point_position(i);
|
||||
// Edge lines.
|
||||
draw_line(get_view_pos(Vector2(min_x, first_point.y)) + Vector2(0.5, 0), get_view_pos(first_point), p_edge_line_color, LINE_WIDTH, true);
|
||||
draw_line(get_view_pos(last_point), get_view_pos(Vector2(max_x, last_point.y)) - Vector2(1.5, 0), p_edge_line_color, LINE_WIDTH, true);
|
||||
|
||||
Vector2 pos = a;
|
||||
Vector2 prev_pos = a;
|
||||
// Draw section by section, so that we get maximum precision near points.
|
||||
// It's an accurate representation, but slower than using the baked one.
|
||||
for (int i = 1; i < curve->get_point_count(); ++i) {
|
||||
Vector2 a = curve->get_point_position(i - 1);
|
||||
Vector2 b = curve->get_point_position(i);
|
||||
|
||||
float scaled_step = step / scaling.x;
|
||||
float samples = (b.x - a.x) / scaled_step;
|
||||
Vector2 pos = a;
|
||||
Vector2 prev_pos = a;
|
||||
|
||||
for (int j = 1; j < samples; j++) {
|
||||
float x = j * scaled_step;
|
||||
pos.x = a.x + x;
|
||||
pos.y = curve.sample_local_nocheck(i - 1, x);
|
||||
plot_func(prev_pos * scaling, pos * scaling, true);
|
||||
prev_pos = pos;
|
||||
}
|
||||
float samples = (b.x - a.x) / world_step_size;
|
||||
|
||||
plot_func(prev_pos * scaling, b * scaling, true);
|
||||
for (int j = 1; j < samples; j++) {
|
||||
float x = j * world_step_size;
|
||||
pos.x = a.x + x;
|
||||
pos.y = curve->sample_local_nocheck(i - 1, x);
|
||||
draw_line(get_view_pos(prev_pos), get_view_pos(pos), p_line_color, LINE_WIDTH, true);
|
||||
prev_pos = pos;
|
||||
}
|
||||
|
||||
draw_line(get_view_pos(prev_pos), get_view_pos(b), p_line_color, LINE_WIDTH, true);
|
||||
}
|
||||
}
|
||||
|
||||
struct CanvasItemPlotCurve {
|
||||
CanvasItem &ci;
|
||||
Color color1;
|
||||
Color color2;
|
||||
|
||||
CanvasItemPlotCurve(CanvasItem &p_ci, Color p_color1, Color p_color2) :
|
||||
ci(p_ci),
|
||||
color1(p_color1),
|
||||
color2(p_color2) {}
|
||||
|
||||
void operator()(Vector2 pos0, Vector2 pos1, bool in_definition) {
|
||||
ci.draw_line(pos0, pos1, in_definition ? color1 : color2, 0.5, true);
|
||||
}
|
||||
};
|
||||
|
||||
void CurveEdit::_redraw() {
|
||||
if (curve.is_null()) {
|
||||
return;
|
||||
@@ -784,7 +776,7 @@ void CurveEdit::_redraw() {
|
||||
Vector2 view_size = get_rect().size;
|
||||
draw_style_box(get_theme_stylebox(SceneStringName(panel), SNAME("Tree")), Rect2(Point2(), view_size));
|
||||
|
||||
// Draw snapping grid, then primary grid.
|
||||
// Draw primary grid.
|
||||
draw_set_transform_matrix(_world_to_view);
|
||||
|
||||
Vector2 min_edge = get_world_pos(Vector2(0, view_size.y));
|
||||
@@ -794,15 +786,15 @@ void CurveEdit::_redraw() {
|
||||
const Color grid_color = get_theme_color(SNAME("mono_color"), EditorStringName(Editor)) * Color(1, 1, 1, 0.1);
|
||||
|
||||
const Vector2i grid_steps = Vector2i(4, 2);
|
||||
const Vector2 step_size = Vector2(1, curve->get_range()) / grid_steps;
|
||||
const Vector2 step_size = Vector2(curve->get_domain_range(), curve->get_value_range()) / grid_steps;
|
||||
|
||||
draw_line(Vector2(min_edge.x, curve->get_min_value()), Vector2(max_edge.x, curve->get_min_value()), grid_color_primary);
|
||||
draw_line(Vector2(max_edge.x, curve->get_max_value()), Vector2(min_edge.x, curve->get_max_value()), grid_color_primary);
|
||||
draw_line(Vector2(0, min_edge.y), Vector2(0, max_edge.y), grid_color_primary);
|
||||
draw_line(Vector2(1, max_edge.y), Vector2(1, min_edge.y), grid_color_primary);
|
||||
draw_line(Vector2(curve->get_min_domain(), min_edge.y), Vector2(curve->get_min_domain(), max_edge.y), grid_color_primary);
|
||||
draw_line(Vector2(curve->get_max_domain(), max_edge.y), Vector2(curve->get_max_domain(), min_edge.y), grid_color_primary);
|
||||
|
||||
for (int i = 1; i < grid_steps.x; i++) {
|
||||
real_t x = i * step_size.x;
|
||||
real_t x = curve->get_min_domain() + i * step_size.x;
|
||||
draw_line(Vector2(x, min_edge.y), Vector2(x, max_edge.y), grid_color);
|
||||
}
|
||||
|
||||
@@ -819,30 +811,26 @@ void CurveEdit::_redraw() {
|
||||
float font_height = font->get_height(font_size);
|
||||
Color text_color = get_theme_color(SceneStringName(font_color), EditorStringName(Editor));
|
||||
|
||||
int pad = Math::round(2 * EDSCALE);
|
||||
|
||||
for (int i = 0; i <= grid_steps.x; ++i) {
|
||||
real_t x = i * step_size.x;
|
||||
draw_string(font, get_view_pos(Vector2(x - step_size.x / 2, curve->get_min_value())) + Vector2(0, font_height - Math::round(2 * EDSCALE)), String::num(x, 2), HORIZONTAL_ALIGNMENT_CENTER, get_view_pos(Vector2(step_size.x, 0)).x, font_size, text_color);
|
||||
real_t x = curve->get_min_domain() + i * step_size.x;
|
||||
draw_string(font, get_view_pos(Vector2(x, curve->get_min_value())) + Vector2(pad, font_height - pad), String::num(x, 2), HORIZONTAL_ALIGNMENT_CENTER, -1, font_size, text_color);
|
||||
}
|
||||
|
||||
for (int i = 0; i <= grid_steps.y; ++i) {
|
||||
real_t y = curve->get_min_value() + i * step_size.y;
|
||||
draw_string(font, get_view_pos(Vector2(0, y)) + Vector2(2, -2), String::num(y, 2), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color);
|
||||
draw_string(font, get_view_pos(Vector2(curve->get_min_domain(), y)) + Vector2(pad, -pad), String::num(y, 2), HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, text_color);
|
||||
}
|
||||
|
||||
// Draw curve.
|
||||
|
||||
// An unusual transform so we can offset the curve before scaling it up, allowing the curve to be antialiased.
|
||||
// The scaling up ensures that the curve rendering doesn't break when we use a quad line to draw it.
|
||||
draw_set_transform_matrix(Transform2D(0, get_view_pos(Vector2(0, 0))));
|
||||
// Draw curve in view coordinates. Curve world-to-view point conversion happens in plot_curve_accurate().
|
||||
|
||||
const Color line_color = get_theme_color(SceneStringName(font_color), EditorStringName(Editor));
|
||||
const Color edge_line_color = get_theme_color(SceneStringName(font_color), EditorStringName(Editor)) * Color(1, 1, 1, 0.75);
|
||||
|
||||
CanvasItemPlotCurve plot_func(*this, line_color, edge_line_color);
|
||||
plot_curve_accurate(**curve, 2.f, (get_view_pos(Vector2(1, curve->get_max_value())) - get_view_pos(Vector2(0, curve->get_min_value()))) / Vector2(1, curve->get_range()), plot_func);
|
||||
plot_curve_accurate(STEP_SIZE, line_color, edge_line_color);
|
||||
|
||||
// Draw points, except for the selected one.
|
||||
draw_set_transform_matrix(Transform2D());
|
||||
|
||||
bool shift_pressed = Input::get_singleton()->is_key_pressed(Key::SHIFT);
|
||||
|
||||
@@ -934,8 +922,8 @@ void CurveEdit::_redraw() {
|
||||
draw_set_transform_matrix(_world_to_view);
|
||||
|
||||
if (Input::get_singleton()->is_key_pressed(Key::ALT) && grabbing != GRAB_NONE && selected_tangent_index == TANGENT_NONE) {
|
||||
float prev_point_offset = (selected_index > 0) ? curve->get_point_position(selected_index - 1).x : 0.0;
|
||||
float next_point_offset = (selected_index < curve->get_point_count() - 1) ? curve->get_point_position(selected_index + 1).x : 1.0;
|
||||
float prev_point_offset = (selected_index > 0) ? curve->get_point_position(selected_index - 1).x : curve->get_min_domain();
|
||||
float next_point_offset = (selected_index < curve->get_point_count() - 1) ? curve->get_point_position(selected_index + 1).x : curve->get_max_domain();
|
||||
|
||||
draw_line(Vector2(prev_point_offset, curve->get_min_value()), Vector2(prev_point_offset, curve->get_max_value()), Color(point_color, 0.6));
|
||||
draw_line(Vector2(next_point_offset, curve->get_min_value()), Vector2(next_point_offset, curve->get_max_value()), Color(point_color, 0.6));
|
||||
@@ -943,7 +931,7 @@ void CurveEdit::_redraw() {
|
||||
|
||||
if (shift_pressed && grabbing != GRAB_NONE && selected_tangent_index == TANGENT_NONE) {
|
||||
draw_line(Vector2(initial_grab_pos.x, curve->get_min_value()), Vector2(initial_grab_pos.x, curve->get_max_value()), get_theme_color(SNAME("axis_x_color"), EditorStringName(Editor)).darkened(0.4));
|
||||
draw_line(Vector2(0, initial_grab_pos.y), Vector2(1, initial_grab_pos.y), get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor)).darkened(0.4));
|
||||
draw_line(Vector2(curve->get_min_domain(), initial_grab_pos.y), Vector2(curve->get_max_domain(), initial_grab_pos.y), get_theme_color(SNAME("axis_y_color"), EditorStringName(Editor)).darkened(0.4));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1079,15 +1067,15 @@ Ref<Texture2D> CurvePreviewGenerator::generate(const Ref<Resource> &p_from, cons
|
||||
Color line_color = EditorInterface::get_singleton()->get_editor_theme()->get_color(SceneStringName(font_color), EditorStringName(Editor));
|
||||
|
||||
// Set the first pixel of the thumbnail.
|
||||
float v = (curve->sample_baked(0) - curve->get_min_value()) / curve->get_range();
|
||||
float v = (curve->sample_baked(curve->get_min_domain()) - curve->get_min_value()) / curve->get_value_range();
|
||||
int y = CLAMP(im.get_height() - v * im.get_height(), 0, im.get_height() - 1);
|
||||
im.set_pixel(0, y, line_color);
|
||||
|
||||
// Plot a line towards the next point.
|
||||
int prev_y = y;
|
||||
for (int x = 1; x < im.get_width(); ++x) {
|
||||
float t = static_cast<float>(x) / im.get_width();
|
||||
v = (curve->sample_baked(t) - curve->get_min_value()) / curve->get_range();
|
||||
float t = static_cast<float>(x) / im.get_width() * curve->get_domain_range() + curve->get_min_domain();
|
||||
v = (curve->sample_baked(t) - curve->get_min_value()) / curve->get_value_range();
|
||||
y = CLAMP(im.get_height() - v * im.get_height(), 0, im.get_height() - 1);
|
||||
|
||||
Vector<Point2i> points = Geometry2D::bresenham_line(Point2i(x - 1, prev_y), Point2i(x, y));
|
||||
|
||||
@@ -78,14 +78,14 @@ private:
|
||||
virtual void gui_input(const Ref<InputEvent> &p_event) override;
|
||||
void _curve_changed();
|
||||
|
||||
int get_point_at(Vector2 p_pos) const;
|
||||
TangentIndex get_tangent_at(Vector2 p_pos) const;
|
||||
int get_point_at(const Vector2 &p_pos) const;
|
||||
TangentIndex get_tangent_at(const Vector2 &p_pos) const;
|
||||
|
||||
float get_offset_without_collision(int p_current_index, float p_offset, bool p_prioritize_right = true);
|
||||
|
||||
void add_point(Vector2 p_pos);
|
||||
void add_point(const Vector2 &p_pos);
|
||||
void remove_point(int p_index);
|
||||
void set_point_position(int p_index, Vector2 p_pos);
|
||||
void set_point_position(int p_index, const Vector2 &p_pos);
|
||||
|
||||
void set_point_tangents(int p_index, float p_left, float p_right);
|
||||
void set_point_left_tangent(int p_index, float p_tangent);
|
||||
@@ -94,17 +94,20 @@ private:
|
||||
|
||||
void update_view_transform();
|
||||
|
||||
void plot_curve_accurate(float p_step, const Color &p_line_color, const Color &p_edge_line_color);
|
||||
|
||||
void set_selected_index(int p_index);
|
||||
void set_selected_tangent_index(TangentIndex p_tangent);
|
||||
|
||||
Vector2 get_tangent_view_pos(int p_index, TangentIndex p_tangent) const;
|
||||
Vector2 get_view_pos(Vector2 p_world_pos) const;
|
||||
Vector2 get_world_pos(Vector2 p_view_pos) const;
|
||||
Vector2 get_view_pos(const Vector2 &p_world_pos) const;
|
||||
Vector2 get_world_pos(const Vector2 &p_view_pos) const;
|
||||
|
||||
void _redraw();
|
||||
|
||||
private:
|
||||
const float ASPECT_RATIO = 6.f / 13.f;
|
||||
const float LINE_WIDTH = 0.5f;
|
||||
const int STEP_SIZE = 2; // Number of pixels between plot points.
|
||||
|
||||
Transform2D _world_to_view;
|
||||
|
||||
@@ -136,9 +139,9 @@ private:
|
||||
};
|
||||
GrabMode grabbing = GRAB_NONE;
|
||||
Vector2 initial_grab_pos;
|
||||
int initial_grab_index;
|
||||
float initial_grab_left_tangent;
|
||||
float initial_grab_right_tangent;
|
||||
int initial_grab_index = -1;
|
||||
float initial_grab_left_tangent = 0;
|
||||
float initial_grab_right_tangent = 0;
|
||||
|
||||
bool snap_enabled = false;
|
||||
int snap_count = 10;
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "core/math/math_funcs.h"
|
||||
|
||||
const char *Curve::SIGNAL_RANGE_CHANGED = "range_changed";
|
||||
const char *Curve::SIGNAL_DOMAIN_CHANGED = "domain_changed";
|
||||
|
||||
Curve::Curve() {
|
||||
}
|
||||
@@ -56,14 +57,11 @@ void Curve::set_point_count(int p_count) {
|
||||
}
|
||||
|
||||
int Curve::_add_point(Vector2 p_position, real_t p_left_tangent, real_t p_right_tangent, TangentMode p_left_mode, TangentMode p_right_mode) {
|
||||
// Add a point and preserve order
|
||||
// Add a point and preserve order.
|
||||
|
||||
// Curve bounds is in 0..1
|
||||
if (p_position.x > MAX_X) {
|
||||
p_position.x = MAX_X;
|
||||
} else if (p_position.x < MIN_X) {
|
||||
p_position.x = MIN_X;
|
||||
}
|
||||
// Points must remain within the given value and domain ranges.
|
||||
p_position.x = CLAMP(p_position.x, _min_domain, _max_domain);
|
||||
p_position.y = CLAMP(p_position.y, _min_value, _max_value);
|
||||
|
||||
int ret = -1;
|
||||
|
||||
@@ -88,11 +86,11 @@ int Curve::_add_point(Vector2 p_position, real_t p_left_tangent, real_t p_right_
|
||||
int i = get_index(p_position.x);
|
||||
|
||||
if (i == 0 && p_position.x < _points[0].position.x) {
|
||||
// Insert before anything else
|
||||
// Insert before anything else.
|
||||
_points.insert(0, Point(p_position, p_left_tangent, p_right_tangent, p_left_mode, p_right_mode));
|
||||
ret = 0;
|
||||
} else {
|
||||
// Insert between i and i+1
|
||||
// Insert between i and i+1.
|
||||
++i;
|
||||
_points.insert(i, Point(p_position, p_left_tangent, p_right_tangent, p_left_mode, p_right_mode));
|
||||
ret = i;
|
||||
@@ -121,7 +119,7 @@ int Curve::add_point_no_update(Vector2 p_position, real_t p_left_tangent, real_t
|
||||
}
|
||||
|
||||
int Curve::get_index(real_t p_offset) const {
|
||||
// Lower-bound float binary search
|
||||
// Lower-bound float binary search.
|
||||
|
||||
int imin = 0;
|
||||
int imax = _points.size() - 1;
|
||||
@@ -134,16 +132,14 @@ int Curve::get_index(real_t p_offset) const {
|
||||
|
||||
if (a < p_offset && b < p_offset) {
|
||||
imin = m;
|
||||
|
||||
} else if (a > p_offset) {
|
||||
imax = m;
|
||||
|
||||
} else {
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
// Will happen if the offset is out of bounds
|
||||
// Will happen if the offset is out of bounds.
|
||||
if (p_offset > _points[imax].position.x) {
|
||||
return imax;
|
||||
}
|
||||
@@ -305,30 +301,80 @@ void Curve::update_auto_tangents(int p_index) {
|
||||
}
|
||||
}
|
||||
|
||||
#define MIN_X_RANGE 0.01
|
||||
#define MIN_Y_RANGE 0.01
|
||||
|
||||
void Curve::set_min_value(real_t p_min) {
|
||||
if (_minmax_set_once & 0b11 && p_min > _max_value - MIN_Y_RANGE) {
|
||||
_min_value = _max_value - MIN_Y_RANGE;
|
||||
} else {
|
||||
_minmax_set_once |= 0b10; // first bit is "min set"
|
||||
_min_value = p_min;
|
||||
Array Curve::get_limits() const {
|
||||
Array output;
|
||||
output.resize(4);
|
||||
|
||||
output[0] = _min_value;
|
||||
output[1] = _max_value;
|
||||
output[2] = _min_domain;
|
||||
output[3] = _max_domain;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
void Curve::set_limits(const Array &p_input) {
|
||||
if (p_input.size() != 4) {
|
||||
WARN_PRINT_ED(vformat(R"(Could not find Curve limit values when deserializing "%s". Resetting limits to default values.)", this->get_path()));
|
||||
_min_value = 0;
|
||||
_max_value = 1;
|
||||
_min_domain = 0;
|
||||
_max_domain = 1;
|
||||
return;
|
||||
}
|
||||
// Note: min and max are indicative values,
|
||||
// it's still possible that existing points are out of range at this point.
|
||||
|
||||
// Do not use setters because we don't want to enforce their logical constraints during deserialization.
|
||||
_min_value = p_input[0];
|
||||
_max_value = p_input[1];
|
||||
_min_domain = p_input[2];
|
||||
_max_domain = p_input[3];
|
||||
}
|
||||
|
||||
void Curve::set_min_value(real_t p_min) {
|
||||
_min_value = MIN(p_min, _max_value - MIN_Y_RANGE);
|
||||
|
||||
for (const Point &p : _points) {
|
||||
_min_value = MIN(_min_value, p.position.y);
|
||||
}
|
||||
|
||||
emit_signal(SNAME(SIGNAL_RANGE_CHANGED));
|
||||
}
|
||||
|
||||
void Curve::set_max_value(real_t p_max) {
|
||||
if (_minmax_set_once & 0b11 && p_max < _min_value + MIN_Y_RANGE) {
|
||||
_max_value = _min_value + MIN_Y_RANGE;
|
||||
} else {
|
||||
_minmax_set_once |= 0b01; // second bit is "max set"
|
||||
_max_value = p_max;
|
||||
_max_value = MAX(p_max, _min_value + MIN_Y_RANGE);
|
||||
|
||||
for (const Point &p : _points) {
|
||||
_max_value = MAX(_max_value, p.position.y);
|
||||
}
|
||||
|
||||
emit_signal(SNAME(SIGNAL_RANGE_CHANGED));
|
||||
}
|
||||
|
||||
void Curve::set_min_domain(real_t p_min) {
|
||||
_min_domain = MIN(p_min, _max_domain - MIN_X_RANGE);
|
||||
|
||||
if (_points.size() > 0 && _min_domain > _points[0].position.x) {
|
||||
_min_domain = _points[0].position.x;
|
||||
}
|
||||
|
||||
mark_dirty();
|
||||
emit_signal(SNAME(SIGNAL_DOMAIN_CHANGED));
|
||||
}
|
||||
|
||||
void Curve::set_max_domain(real_t p_max) {
|
||||
_max_domain = MAX(p_max, _min_domain + MIN_X_RANGE);
|
||||
|
||||
if (_points.size() > 0 && _max_domain < _points[_points.size() - 1].position.x) {
|
||||
_max_domain = _points[_points.size() - 1].position.x;
|
||||
}
|
||||
|
||||
mark_dirty();
|
||||
emit_signal(SNAME(SIGNAL_DOMAIN_CHANGED));
|
||||
}
|
||||
|
||||
real_t Curve::sample(real_t p_offset) const {
|
||||
if (_points.size() == 0) {
|
||||
return 0;
|
||||
@@ -370,7 +416,7 @@ real_t Curve::sample_local_nocheck(int p_index, real_t p_local_offset) const {
|
||||
* d1 == d2 == d3 == d / 3
|
||||
*/
|
||||
|
||||
// Control points are chosen at equal distances
|
||||
// Control points are chosen at equal distances.
|
||||
real_t d = b.position.x - a.position.x;
|
||||
if (Math::is_zero_approx(d)) {
|
||||
return b.position.y;
|
||||
@@ -458,7 +504,7 @@ void Curve::bake() {
|
||||
_baked_cache.resize(_bake_resolution);
|
||||
|
||||
for (int i = 1; i < _bake_resolution - 1; ++i) {
|
||||
real_t x = i / static_cast<real_t>(_bake_resolution - 1);
|
||||
real_t x = get_domain_range() * i / static_cast<real_t>(_bake_resolution - 1) + _min_domain;
|
||||
real_t y = sample(x);
|
||||
_baked_cache.write[i] = y;
|
||||
}
|
||||
@@ -480,11 +526,11 @@ void Curve::set_bake_resolution(int p_resolution) {
|
||||
|
||||
real_t Curve::sample_baked(real_t p_offset) const {
|
||||
if (_baked_cache_dirty) {
|
||||
// Last-second bake if not done already
|
||||
// Last-second bake if not done already.
|
||||
const_cast<Curve *>(this)->bake();
|
||||
}
|
||||
|
||||
// Special cases if the cache is too small
|
||||
// Special cases if the cache is too small.
|
||||
if (_baked_cache.size() == 0) {
|
||||
if (_points.size() == 0) {
|
||||
return 0;
|
||||
@@ -494,8 +540,8 @@ real_t Curve::sample_baked(real_t p_offset) const {
|
||||
return _baked_cache[0];
|
||||
}
|
||||
|
||||
// Get interpolation index
|
||||
real_t fi = p_offset * (_baked_cache.size() - 1);
|
||||
// Get interpolation index.
|
||||
real_t fi = (p_offset - _min_domain) / get_domain_range() * (_baked_cache.size() - 1);
|
||||
int i = Math::floor(fi);
|
||||
if (i < 0) {
|
||||
i = 0;
|
||||
@@ -505,7 +551,7 @@ real_t Curve::sample_baked(real_t p_offset) const {
|
||||
fi = 0;
|
||||
}
|
||||
|
||||
// Sample
|
||||
// Sample.
|
||||
if (i + 1 < _baked_cache.size()) {
|
||||
real_t t = fi - i;
|
||||
return Math::lerp(_baked_cache[i], _baked_cache[i + 1], t);
|
||||
@@ -628,6 +674,14 @@ void Curve::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_min_value", "min"), &Curve::set_min_value);
|
||||
ClassDB::bind_method(D_METHOD("get_max_value"), &Curve::get_max_value);
|
||||
ClassDB::bind_method(D_METHOD("set_max_value", "max"), &Curve::set_max_value);
|
||||
ClassDB::bind_method(D_METHOD("get_value_range"), &Curve::get_value_range);
|
||||
ClassDB::bind_method(D_METHOD("get_min_domain"), &Curve::get_min_domain);
|
||||
ClassDB::bind_method(D_METHOD("set_min_domain", "min"), &Curve::set_min_domain);
|
||||
ClassDB::bind_method(D_METHOD("get_max_domain"), &Curve::get_max_domain);
|
||||
ClassDB::bind_method(D_METHOD("set_max_domain", "max"), &Curve::set_max_domain);
|
||||
ClassDB::bind_method(D_METHOD("get_domain_range"), &Curve::get_domain_range);
|
||||
ClassDB::bind_method(D_METHOD("_get_limits"), &Curve::get_limits);
|
||||
ClassDB::bind_method(D_METHOD("_set_limits", "data"), &Curve::set_limits);
|
||||
ClassDB::bind_method(D_METHOD("clean_dupes"), &Curve::clean_dupes);
|
||||
ClassDB::bind_method(D_METHOD("bake"), &Curve::bake);
|
||||
ClassDB::bind_method(D_METHOD("get_bake_resolution"), &Curve::get_bake_resolution);
|
||||
@@ -635,13 +689,17 @@ void Curve::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_get_data"), &Curve::get_data);
|
||||
ClassDB::bind_method(D_METHOD("_set_data", "data"), &Curve::set_data);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_value", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_min_value", "get_min_value");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_value", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_max_value", "get_max_value");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_domain", PROPERTY_HINT_RANGE, "-1024,1024,0.01,or_greater,or_less", PROPERTY_USAGE_EDITOR), "set_min_domain", "get_min_domain");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_domain", PROPERTY_HINT_RANGE, "-1024,1024,0.01,or_greater,or_less", PROPERTY_USAGE_EDITOR), "set_max_domain", "get_max_domain");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "min_value", PROPERTY_HINT_RANGE, "-1024,1024,0.01,or_greater,or_less", PROPERTY_USAGE_EDITOR), "set_min_value", "get_min_value");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_value", PROPERTY_HINT_RANGE, "-1024,1024,0.01,or_greater,or_less", PROPERTY_USAGE_EDITOR), "set_max_value", "get_max_value");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::NIL, "_limits", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_limits", "_get_limits");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_resolution", PROPERTY_HINT_RANGE, "1,1000,1"), "set_bake_resolution", "get_bake_resolution");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
|
||||
ADD_ARRAY_COUNT("Points", "point_count", "set_point_count", "get_point_count", "point_");
|
||||
|
||||
ADD_SIGNAL(MethodInfo(SIGNAL_RANGE_CHANGED));
|
||||
ADD_SIGNAL(MethodInfo(SIGNAL_DOMAIN_CHANGED));
|
||||
|
||||
BIND_ENUM_CONSTANT(TANGENT_FREE);
|
||||
BIND_ENUM_CONSTANT(TANGENT_LINEAR);
|
||||
@@ -852,7 +910,7 @@ void Curve2D::_bake() const {
|
||||
return;
|
||||
}
|
||||
|
||||
// Tessellate curve to (almost) even length segments
|
||||
// Tessellate curve to (almost) even length segments.
|
||||
{
|
||||
Vector<RBMap<real_t, Vector2>> midpoints = _tessellate_even_length(10, bake_interval);
|
||||
|
||||
@@ -1592,7 +1650,7 @@ void Curve3D::_bake() const {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1: Tessellate curve to (almost) even length segments
|
||||
// Step 1: Tessellate curve to (almost) even length segments.
|
||||
{
|
||||
Vector<RBMap<real_t, Vector3>> midpoints = _tessellate_even_length(10, bake_interval);
|
||||
|
||||
@@ -1653,7 +1711,7 @@ void Curve3D::_bake() const {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2: Calculate the up vectors and the whole local reference frame
|
||||
// Step 2: Calculate the up vectors and the whole local reference frame.
|
||||
//
|
||||
// See Dougan, Carl. "The parallel transport frame." Game Programming Gems 2 (2001): 215-219.
|
||||
// for an example discussing about why not the Frenet frame.
|
||||
@@ -1689,7 +1747,7 @@ void Curve3D::_bake() const {
|
||||
Basis rotate;
|
||||
rotate.rotate_to_align(-frame_prev.get_column(2), forward);
|
||||
frame = rotate * frame_prev;
|
||||
frame.orthonormalize(); // guard against float error accumulation
|
||||
frame.orthonormalize(); // Guard against float error accumulation.
|
||||
|
||||
up_write[idx] = frame.get_column(1);
|
||||
frame_prev = frame;
|
||||
@@ -1941,7 +1999,7 @@ real_t Curve3D::sample_baked_tilt(real_t p_offset) const {
|
||||
return baked_tilt_cache.get(0);
|
||||
}
|
||||
|
||||
p_offset = CLAMP(p_offset, 0.0, get_baked_length()); // PathFollower implement wrapping logic
|
||||
p_offset = CLAMP(p_offset, 0.0, get_baked_length()); // PathFollower implement wrapping logic.
|
||||
|
||||
Curve3D::Interval interval = _find_interval(p_offset);
|
||||
return _sample_baked_tilt(interval);
|
||||
|
||||
@@ -38,10 +38,8 @@ class Curve : public Resource {
|
||||
GDCLASS(Curve, Resource);
|
||||
|
||||
public:
|
||||
static const int MIN_X = 0.f;
|
||||
static const int MAX_X = 1.f;
|
||||
|
||||
static const char *SIGNAL_RANGE_CHANGED;
|
||||
static const char *SIGNAL_DOMAIN_CHANGED;
|
||||
|
||||
enum TangentMode {
|
||||
TANGENT_FREE = 0,
|
||||
@@ -101,11 +99,18 @@ public:
|
||||
|
||||
real_t get_min_value() const { return _min_value; }
|
||||
void set_min_value(real_t p_min);
|
||||
|
||||
real_t get_max_value() const { return _max_value; }
|
||||
void set_max_value(real_t p_max);
|
||||
real_t get_value_range() const { return _max_value - _min_value; }
|
||||
|
||||
real_t get_range() const { return _max_value - _min_value; }
|
||||
real_t get_min_domain() const { return _min_domain; }
|
||||
void set_min_domain(real_t p_min);
|
||||
real_t get_max_domain() const { return _max_domain; }
|
||||
void set_max_domain(real_t p_max);
|
||||
real_t get_domain_range() const { return _max_domain - _min_domain; }
|
||||
|
||||
Array get_limits() const;
|
||||
void set_limits(const Array &p_input);
|
||||
|
||||
real_t sample(real_t p_offset) const;
|
||||
real_t sample_local_nocheck(int p_index, real_t p_local_offset) const;
|
||||
@@ -156,7 +161,8 @@ private:
|
||||
int _bake_resolution = 100;
|
||||
real_t _min_value = 0.0;
|
||||
real_t _max_value = 1.0;
|
||||
int _minmax_set_once = 0b00; // Encodes whether min and max have been set a first time, first bit for min and second for max.
|
||||
real_t _min_domain = 0.0;
|
||||
real_t _max_domain = 1.0;
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(Curve::TangentMode)
|
||||
|
||||
@@ -55,7 +55,7 @@ TEST_CASE("[Curve] Default curve") {
|
||||
"Default curve should return the expected value at offset 1.0.");
|
||||
}
|
||||
|
||||
TEST_CASE("[Curve] Custom curve with free tangents") {
|
||||
TEST_CASE("[Curve] Custom unit curve with free tangents") {
|
||||
Ref<Curve> curve = memnew(Curve);
|
||||
// "Sawtooth" curve with an open ending towards the 1.0 offset.
|
||||
curve->add_point(Vector2(0, 0));
|
||||
@@ -136,7 +136,90 @@ TEST_CASE("[Curve] Custom curve with free tangents") {
|
||||
"Custom free curve should return the expected baked value at offset 0.6 after clearing all points.");
|
||||
}
|
||||
|
||||
TEST_CASE("[Curve] Custom curve with linear tangents") {
|
||||
TEST_CASE("[Curve] Custom non-unit curve with free tangents") {
|
||||
Ref<Curve> curve = memnew(Curve);
|
||||
curve->set_min_domain(-100.0);
|
||||
curve->set_max_domain(100.0);
|
||||
// "Sawtooth" curve with an open ending towards the 100 offset.
|
||||
curve->add_point(Vector2(-100, 0));
|
||||
curve->add_point(Vector2(-50, 1));
|
||||
curve->add_point(Vector2(0, 0));
|
||||
curve->add_point(Vector2(50, 1));
|
||||
curve->set_bake_resolution(11);
|
||||
|
||||
CHECK_MESSAGE(
|
||||
Math::is_zero_approx(curve->get_point_left_tangent(0)),
|
||||
"get_point_left_tangent() should return the expected value for point index 0.");
|
||||
CHECK_MESSAGE(
|
||||
Math::is_zero_approx(curve->get_point_right_tangent(0)),
|
||||
"get_point_right_tangent() should return the expected value for point index 0.");
|
||||
CHECK_MESSAGE(
|
||||
curve->get_point_left_mode(0) == Curve::TangentMode::TANGENT_FREE,
|
||||
"get_point_left_mode() should return the expected value for point index 0.");
|
||||
CHECK_MESSAGE(
|
||||
curve->get_point_right_mode(0) == Curve::TangentMode::TANGENT_FREE,
|
||||
"get_point_right_mode() should return the expected value for point index 0.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
curve->get_point_count() == 4,
|
||||
"Custom free curve should contain the expected number of points.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
Math::is_zero_approx(curve->sample(-200)),
|
||||
"Custom free curve should return the expected value at offset -200.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(0.1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.352),
|
||||
"Custom free curve should return the expected value at offset equivalent to a unit curve's 0.1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(0.4 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.352),
|
||||
"Custom free curve should return the expected value at offset equivalent to a unit curve's 0.4.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(0.7 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.896),
|
||||
"Custom free curve should return the expected value at offset equivalent to a unit curve's 0.7.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(1),
|
||||
"Custom free curve should return the expected value at offset equivalent to a unit curve's 1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(2 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(1),
|
||||
"Custom free curve should return the expected value at offset equivalent to a unit curve's 2.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
Math::is_zero_approx(curve->sample_baked(-200)),
|
||||
"Custom free curve should return the expected baked value at offset equivalent to a unit curve's -0.1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(0.1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.352),
|
||||
"Custom free curve should return the expected baked value at offset equivalent to a unit curve's 0.1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(0.4 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.352),
|
||||
"Custom free curve should return the expected baked value at offset equivalent to a unit curve's 0.4.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(0.7 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.896),
|
||||
"Custom free curve should return the expected baked value at offset equivalent to a unit curve's 0.7.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(1),
|
||||
"Custom free curve should return the expected baked value at offset equivalent to a unit curve's 1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(2 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(1),
|
||||
"Custom free curve should return the expected baked value at offset equivalent to a unit curve's 2.");
|
||||
|
||||
curve->remove_point(1);
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(0.1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(0),
|
||||
"Custom free curve should return the expected value at offset equivalent to a unit curve's 0.1 after removing point at index 1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(0.1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(0),
|
||||
"Custom free curve should return the expected baked value at offset equivalent to a unit curve's 0.1 after removing point at index 1.");
|
||||
|
||||
curve->clear_points();
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(0.6 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(0),
|
||||
"Custom free curve should return the expected value at offset 0.6 after clearing all points.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(0.6 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(0),
|
||||
"Custom free curve should return the expected baked value at offset 0.6 after clearing all points.");
|
||||
}
|
||||
|
||||
TEST_CASE("[Curve] Custom unit curve with linear tangents") {
|
||||
Ref<Curve> curve = memnew(Curve);
|
||||
// "Sawtooth" curve with an open ending towards the 1.0 offset.
|
||||
curve->add_point(Vector2(0, 0), 0, 0, Curve::TangentMode::TANGENT_LINEAR, Curve::TangentMode::TANGENT_LINEAR);
|
||||
@@ -219,6 +302,91 @@ TEST_CASE("[Curve] Custom curve with linear tangents") {
|
||||
"Custom free curve should return the expected baked value at offset 0.7 after removing point at invalid index 10.");
|
||||
}
|
||||
|
||||
TEST_CASE("[Curve] Custom non-unit curve with linear tangents") {
|
||||
Ref<Curve> curve = memnew(Curve);
|
||||
curve->set_min_domain(-100.0);
|
||||
curve->set_max_domain(100.0);
|
||||
// "Sawtooth" curve with an open ending towards the 100 offset.
|
||||
curve->add_point(Vector2(-100, 0), 0, 0, Curve::TangentMode::TANGENT_LINEAR, Curve::TangentMode::TANGENT_LINEAR);
|
||||
curve->add_point(Vector2(-50, 1), 0, 0, Curve::TangentMode::TANGENT_LINEAR, Curve::TangentMode::TANGENT_LINEAR);
|
||||
curve->add_point(Vector2(0, 0), 0, 0, Curve::TangentMode::TANGENT_LINEAR, Curve::TangentMode::TANGENT_LINEAR);
|
||||
curve->add_point(Vector2(50, 1), 0, 0, Curve::TangentMode::TANGENT_LINEAR, Curve::TangentMode::TANGENT_LINEAR);
|
||||
|
||||
CHECK_MESSAGE(
|
||||
curve->get_point_left_tangent(3) == doctest::Approx(1.f / 50),
|
||||
"get_point_left_tangent() should return the expected value for point index 3.");
|
||||
CHECK_MESSAGE(
|
||||
Math::is_zero_approx(curve->get_point_right_tangent(3)),
|
||||
"get_point_right_tangent() should return the expected value for point index 3.");
|
||||
CHECK_MESSAGE(
|
||||
curve->get_point_left_mode(3) == Curve::TangentMode::TANGENT_LINEAR,
|
||||
"get_point_left_mode() should return the expected value for point index 3.");
|
||||
CHECK_MESSAGE(
|
||||
curve->get_point_right_mode(3) == Curve::TangentMode::TANGENT_LINEAR,
|
||||
"get_point_right_mode() should return the expected value for point index 3.");
|
||||
|
||||
ERR_PRINT_OFF;
|
||||
CHECK_MESSAGE(
|
||||
Math::is_zero_approx(curve->get_point_right_tangent(300)),
|
||||
"get_point_right_tangent() should return the expected value for invalid point index 300.");
|
||||
CHECK_MESSAGE(
|
||||
curve->get_point_left_mode(-12345) == Curve::TangentMode::TANGENT_FREE,
|
||||
"get_point_left_mode() should return the expected value for invalid point index -12345.");
|
||||
ERR_PRINT_ON;
|
||||
|
||||
CHECK_MESSAGE(
|
||||
curve->get_point_count() == 4,
|
||||
"Custom linear unit curve should contain the expected number of points.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
Math::is_zero_approx(curve->sample(-0.1 * curve->get_domain_range() + curve->get_min_domain())),
|
||||
"Custom linear curve should return the expected value at offset equivalent to a unit curve's -0.1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(0.1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.4),
|
||||
"Custom linear curve should return the expected value at offset equivalent to a unit curve's 0.1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(0.4 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.4),
|
||||
"Custom linear curve should return the expected value at offset equivalent to a unit curve's 0.4.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(0.7 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.8),
|
||||
"Custom linear curve should return the expected value at offset equivalent to a unit curve's 0.7.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(1),
|
||||
"Custom linear curve should return the expected value at offset equivalent to a unit curve's 1.0.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(2 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(1),
|
||||
"Custom linear curve should return the expected value at offset equivalent to a unit curve's 2.0.");
|
||||
|
||||
CHECK_MESSAGE(
|
||||
Math::is_zero_approx(curve->sample_baked(-0.1 * curve->get_domain_range() + curve->get_min_domain())),
|
||||
"Custom linear curve should return the expected baked value at offset equivalent to a unit curve's -0.1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(0.1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.4),
|
||||
"Custom linear curve should return the expected baked value at offset equivalent to a unit curve's 0.1.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(0.4 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.4),
|
||||
"Custom linear curve should return the expected baked value at offset equivalent to a unit curve's 0.4.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(0.7 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.8),
|
||||
"Custom linear curve should return the expected baked value at offset equivalent to a unit curve's 0.7.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(1 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(1),
|
||||
"Custom linear curve should return the expected baked value at offset equivalent to a unit curve's 1.0.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(2 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx(1),
|
||||
"Custom linear curve should return the expected baked value at offset equivalent to a unit curve's 2.0.");
|
||||
|
||||
ERR_PRINT_OFF;
|
||||
curve->remove_point(10);
|
||||
ERR_PRINT_ON;
|
||||
CHECK_MESSAGE(
|
||||
curve->sample(0.7 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.8),
|
||||
"Custom free curve should return the expected value at offset equivalent to a unit curve's 0.7 after removing point at invalid index 10.");
|
||||
CHECK_MESSAGE(
|
||||
curve->sample_baked(0.7 * curve->get_domain_range() + curve->get_min_domain()) == doctest::Approx((real_t)0.8),
|
||||
"Custom free curve should return the expected baked value at offset equivalent to a unit curve's 0.7 after removing point at invalid index 10.");
|
||||
}
|
||||
|
||||
TEST_CASE("[Curve] Straight line offset test") {
|
||||
Ref<Curve> curve = memnew(Curve);
|
||||
curve->add_point(Vector2(0, 0));
|
||||
|
||||
Reference in New Issue
Block a user