| 
									
										
										
										
											2023-01-05 13:25:55 +01:00
										 |  |  | /**************************************************************************/ | 
					
						
							|  |  |  | /*  curve_editor_plugin.cpp                                               */ | 
					
						
							|  |  |  | /**************************************************************************/ | 
					
						
							|  |  |  | /*                         This file is part of:                          */ | 
					
						
							|  |  |  | /*                             GODOT ENGINE                               */ | 
					
						
							|  |  |  | /*                        https://godotengine.org                         */ | 
					
						
							|  |  |  | /**************************************************************************/ | 
					
						
							|  |  |  | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | 
					
						
							|  |  |  | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | 
					
						
							|  |  |  | /*                                                                        */ | 
					
						
							|  |  |  | /* Permission is hereby granted, free of charge, to any person obtaining  */ | 
					
						
							|  |  |  | /* a copy of this software and associated documentation files (the        */ | 
					
						
							|  |  |  | /* "Software"), to deal in the Software without restriction, including    */ | 
					
						
							|  |  |  | /* without limitation the rights to use, copy, modify, merge, publish,    */ | 
					
						
							|  |  |  | /* distribute, sublicense, and/or sell copies of the Software, and to     */ | 
					
						
							|  |  |  | /* permit persons to whom the Software is furnished to do so, subject to  */ | 
					
						
							|  |  |  | /* the following conditions:                                              */ | 
					
						
							|  |  |  | /*                                                                        */ | 
					
						
							|  |  |  | /* The above copyright notice and this permission notice shall be         */ | 
					
						
							|  |  |  | /* included in all copies or substantial portions of the Software.        */ | 
					
						
							|  |  |  | /*                                                                        */ | 
					
						
							|  |  |  | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */ | 
					
						
							|  |  |  | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */ | 
					
						
							|  |  |  | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ | 
					
						
							|  |  |  | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */ | 
					
						
							|  |  |  | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */ | 
					
						
							|  |  |  | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */ | 
					
						
							|  |  |  | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */ | 
					
						
							|  |  |  | /**************************************************************************/ | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | #include "curve_editor_plugin.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "canvas_item_editor_plugin.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-11 18:13:45 +02:00
										 |  |  | #include "core/core_string_names.h"
 | 
					
						
							| 
									
										
										
										
											2020-04-28 15:19:37 +02:00
										 |  |  | #include "core/input/input.h"
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | #include "core/math/geometry_2d.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-11 18:13:45 +02:00
										 |  |  | #include "core/os/keyboard.h"
 | 
					
						
							| 
									
										
										
										
											2023-04-05 17:14:46 +02:00
										 |  |  | #include "editor/editor_interface.h"
 | 
					
						
							| 
									
										
										
										
											2022-02-12 02:46:22 +01:00
										 |  |  | #include "editor/editor_node.h"
 | 
					
						
							| 
									
										
										
										
											2019-12-24 15:17:23 +08:00
										 |  |  | #include "editor/editor_scale.h"
 | 
					
						
							| 
									
										
										
										
											2022-07-31 21:14:15 +03:00
										 |  |  | #include "editor/editor_settings.h"
 | 
					
						
							| 
									
										
										
										
											2022-08-29 12:10:32 +02:00
										 |  |  | #include "editor/editor_undo_redo_manager.h"
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | #include "editor/gui/editor_spin_slider.h"
 | 
					
						
							|  |  |  | #include "scene/gui/flow_container.h"
 | 
					
						
							|  |  |  | #include "scene/gui/menu_button.h"
 | 
					
						
							| 
									
										
										
										
											2023-04-12 21:02:28 +02:00
										 |  |  | #include "scene/gui/popup_menu.h"
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | #include "scene/gui/separator.h"
 | 
					
						
							| 
									
										
										
										
											2017-04-28 20:04:09 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | CurveEdit::CurveEdit() { | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 	set_focus_mode(FOCUS_ALL); | 
					
						
							|  |  |  | 	set_clip_contents(true); | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | void CurveEdit::_bind_methods() { | 
					
						
							|  |  |  | 	ClassDB::bind_method(D_METHOD("set_selected_index", "index"), &CurveEdit::set_selected_index); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | void CurveEdit::set_curve(Ref<Curve> p_curve) { | 
					
						
							|  |  |  | 	if (p_curve == curve) { | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2020-05-14 16:41:43 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	if (curve.is_valid()) { | 
					
						
							|  |  |  | 		curve->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveEdit::_curve_changed)); | 
					
						
							|  |  |  | 		curve->disconnect(Curve::SIGNAL_RANGE_CHANGED, callable_mp(this, &CurveEdit::_curve_changed)); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	curve = p_curve; | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	if (curve.is_valid()) { | 
					
						
							|  |  |  | 		curve->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &CurveEdit::_curve_changed)); | 
					
						
							|  |  |  | 		curve->connect(Curve::SIGNAL_RANGE_CHANGED, callable_mp(this, &CurveEdit::_curve_changed)); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	// Note: if you edit a curve, then set another, and try to undo,
 | 
					
						
							|  |  |  | 	// it will normally apply on the previous curve, but you won't see it.
 | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-05-20 12:38:03 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | Ref<Curve> CurveEdit::get_curve() { | 
					
						
							|  |  |  | 	return curve; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CurveEdit::set_snap_enabled(bool p_enabled) { | 
					
						
							|  |  |  | 	snap_enabled = p_enabled; | 
					
						
							| 
									
										
										
										
											2022-08-13 23:21:24 +02:00
										 |  |  | 	queue_redraw(); | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	if (curve.is_valid()) { | 
					
						
							|  |  |  | 		if (snap_enabled) { | 
					
						
							|  |  |  | 			curve->set_meta(SNAME("_snap_enabled"), true); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			curve->remove_meta(SNAME("_snap_enabled")); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | void CurveEdit::set_snap_count(int p_snap_count) { | 
					
						
							|  |  |  | 	snap_count = p_snap_count; | 
					
						
							|  |  |  | 	queue_redraw(); | 
					
						
							|  |  |  | 	if (curve.is_valid()) { | 
					
						
							|  |  |  | 		if (snap_count != CurveEditor::DEFAULT_SNAP) { | 
					
						
							|  |  |  | 			curve->set_meta(SNAME("_snap_count"), snap_count); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			curve->remove_meta(SNAME("_snap_count")); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | Size2 CurveEdit::get_minimum_size() const { | 
					
						
							|  |  |  | 	return Vector2(64, 135) * EDSCALE; | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | void CurveEdit::_notification(int p_what) { | 
					
						
							| 
									
										
										
										
											2022-02-15 21:44:22 -05:00
										 |  |  | 	switch (p_what) { | 
					
						
							| 
									
										
										
										
											2023-05-31 20:03:04 +02:00
										 |  |  | 		case NOTIFICATION_MOUSE_EXIT: { | 
					
						
							|  |  |  | 			if (hovered_index != -1 || hovered_tangent_index != TANGENT_NONE) { | 
					
						
							|  |  |  | 				hovered_index = -1; | 
					
						
							|  |  |  | 				hovered_tangent_index = TANGENT_NONE; | 
					
						
							|  |  |  | 				queue_redraw(); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-02-15 21:44:22 -05:00
										 |  |  | 		} break; | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 		case NOTIFICATION_THEME_CHANGED: | 
					
						
							| 
									
										
										
										
											2023-04-05 13:04:00 -07:00
										 |  |  | 		case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 			float gizmo_scale = EDITOR_GET("interface/touchscreen/scale_gizmo_handles"); | 
					
						
							|  |  |  | 			point_radius = Math::round(BASE_POINT_RADIUS * get_theme_default_base_scale() * gizmo_scale); | 
					
						
							|  |  |  | 			hover_radius = Math::round(BASE_HOVER_RADIUS * get_theme_default_base_scale() * gizmo_scale); | 
					
						
							|  |  |  | 			tangent_radius = Math::round(BASE_TANGENT_RADIUS * get_theme_default_base_scale() * gizmo_scale); | 
					
						
							|  |  |  | 			tangent_hover_radius = Math::round(BASE_TANGENT_HOVER_RADIUS * get_theme_default_base_scale() * gizmo_scale); | 
					
						
							|  |  |  | 			tangent_length = Math::round(BASE_TANGENT_LENGTH * get_theme_default_base_scale()); | 
					
						
							|  |  |  | 		} break; | 
					
						
							|  |  |  | 		case NOTIFICATION_DRAW: { | 
					
						
							|  |  |  | 			_redraw(); | 
					
						
							|  |  |  | 		} break; | 
					
						
							|  |  |  | 		case NOTIFICATION_VISIBILITY_CHANGED: { | 
					
						
							|  |  |  | 			if (!is_visible()) { | 
					
						
							|  |  |  | 				grabbing = GRAB_NONE; | 
					
						
							| 
									
										
										
										
											2023-04-05 13:04:00 -07:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} break; | 
					
						
							| 
									
										
										
										
											2020-05-14 16:41:43 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | void CurveEdit::gui_input(const Ref<InputEvent> &p_event) { | 
					
						
							|  |  |  | 	ERR_FAIL_COND(p_event.is_null()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Ref<InputEventKey> k = p_event; | 
					
						
							|  |  |  | 	if (k.is_valid()) { | 
					
						
							|  |  |  | 		// Deleting points or making tangents linear.
 | 
					
						
							|  |  |  | 		if (k->is_pressed() && k->get_keycode() == Key::KEY_DELETE) { | 
					
						
							|  |  |  | 			if (selected_tangent_index != TANGENT_NONE) { | 
					
						
							|  |  |  | 				toggle_linear(selected_index, selected_tangent_index); | 
					
						
							|  |  |  | 			} else if (selected_index != -1) { | 
					
						
							|  |  |  | 				if (grabbing == GRAB_ADD) { | 
					
						
							|  |  |  | 					curve->remove_point(selected_index); // Point is temporary, so remove directly from curve.
 | 
					
						
							|  |  |  | 					set_selected_index(-1); | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					remove_point(selected_index); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				grabbing = GRAB_NONE; | 
					
						
							|  |  |  | 				hovered_index = -1; | 
					
						
							|  |  |  | 				hovered_tangent_index = TANGENT_NONE; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			accept_event(); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 		if (k->get_keycode() == Key::SHIFT || k->get_keycode() == Key::ALT) { | 
					
						
							|  |  |  | 			queue_redraw(); // Redraw to show the axes or constraints.
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	Ref<InputEventMouseButton> mb = p_event; | 
					
						
							|  |  |  | 	if (mb.is_valid() && mb->is_pressed()) { | 
					
						
							|  |  |  | 		Vector2 mpos = mb->get_position(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (mb->get_button_index() == MouseButton::RIGHT || mb->get_button_index() == MouseButton::MIDDLE) { | 
					
						
							|  |  |  | 			if (mb->get_button_index() == MouseButton::RIGHT && grabbing == GRAB_MOVE) { | 
					
						
							|  |  |  | 				// Move a point to its old position.
 | 
					
						
							|  |  |  | 				curve->set_point_value(selected_index, initial_grab_pos.y); | 
					
						
							|  |  |  | 				curve->set_point_offset(selected_index, initial_grab_pos.x); | 
					
						
							|  |  |  | 				set_selected_index(initial_grab_index); | 
					
						
							|  |  |  | 				hovered_index = get_point_at(mpos); | 
					
						
							|  |  |  | 				grabbing = GRAB_NONE; | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				// Remove a point or make a tangent linear.
 | 
					
						
							|  |  |  | 				selected_tangent_index = get_tangent_at(mpos); | 
					
						
							|  |  |  | 				if (selected_tangent_index != TANGENT_NONE) { | 
					
						
							|  |  |  | 					toggle_linear(selected_index, selected_tangent_index); | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					int point_to_remove = get_point_at(mpos); | 
					
						
							|  |  |  | 					if (point_to_remove != -1) { | 
					
						
							|  |  |  | 						if (grabbing == GRAB_ADD) { | 
					
						
							|  |  |  | 							curve->remove_point(point_to_remove); // Point is temporary, so remove directly from curve.
 | 
					
						
							|  |  |  | 							set_selected_index(-1); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							remove_point(point_to_remove); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						hovered_index = get_point_at(mpos); | 
					
						
							|  |  |  | 						grabbing = GRAB_NONE; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2020-05-14 16:41:43 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 		// Selecting or creating points.
 | 
					
						
							|  |  |  | 		if (mb->get_button_index() == MouseButton::LEFT) { | 
					
						
							|  |  |  | 			if (grabbing == GRAB_NONE) { | 
					
						
							|  |  |  | 				selected_tangent_index = get_tangent_at(mpos); | 
					
						
							|  |  |  | 				if (selected_tangent_index == TANGENT_NONE) { | 
					
						
							|  |  |  | 					set_selected_index(get_point_at(mpos)); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				queue_redraw(); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 			if (selected_index != -1) { | 
					
						
							|  |  |  | 				// If an existing point/tangent was grabbed, remember a few things about it.
 | 
					
						
							|  |  |  | 				grabbing = GRAB_MOVE; | 
					
						
							|  |  |  | 				initial_grab_pos = curve->get_point_position(selected_index); | 
					
						
							|  |  |  | 				initial_grab_index = selected_index; | 
					
						
							|  |  |  | 				if (selected_index > 0) { | 
					
						
							|  |  |  | 					initial_grab_left_tangent = curve->get_point_left_tangent(selected_index); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if (selected_index < curve->get_point_count() - 1) { | 
					
						
							|  |  |  | 					initial_grab_right_tangent = curve->get_point_right_tangent(selected_index); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} 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())); | 
					
						
							|  |  |  | 				if (snap_enabled || mb->is_ctrl_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 = get_offset_without_collision(selected_index, new_pos.x, mpos.x >= get_view_pos(new_pos).x); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// Add a temporary point for the user to adjust before adding it permanently.
 | 
					
						
							|  |  |  | 				int new_idx = curve->add_point_no_update(new_pos); | 
					
						
							|  |  |  | 				set_selected_index(new_idx); | 
					
						
							|  |  |  | 				grabbing = GRAB_ADD; | 
					
						
							|  |  |  | 				initial_grab_pos = new_pos; | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed()) { | 
					
						
							|  |  |  | 		if (selected_tangent_index != TANGENT_NONE) { | 
					
						
							|  |  |  | 			// Finish moving a tangent control.
 | 
					
						
							|  |  |  | 			if (selected_index == 0) { | 
					
						
							|  |  |  | 				set_point_right_tangent(selected_index, curve->get_point_right_tangent(selected_index)); | 
					
						
							|  |  |  | 			} else if (selected_index == curve->get_point_count() - 1) { | 
					
						
							|  |  |  | 				set_point_left_tangent(selected_index, curve->get_point_left_tangent(selected_index)); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				set_point_tangents(selected_index, curve->get_point_left_tangent(selected_index), curve->get_point_right_tangent(selected_index)); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			grabbing = GRAB_NONE; | 
					
						
							|  |  |  | 		} else if (grabbing == GRAB_MOVE) { | 
					
						
							|  |  |  | 			// Finish moving a point.
 | 
					
						
							|  |  |  | 			set_point_position(selected_index, curve->get_point_position(selected_index)); | 
					
						
							|  |  |  | 			grabbing = GRAB_NONE; | 
					
						
							|  |  |  | 		} else if (grabbing == GRAB_ADD) { | 
					
						
							|  |  |  | 			// Finish inserting a new point. Remove the temporary point and insert a permanent one in its place.
 | 
					
						
							|  |  |  | 			Vector2 new_pos = curve->get_point_position(selected_index); | 
					
						
							|  |  |  | 			curve->remove_point(selected_index); | 
					
						
							|  |  |  | 			add_point(new_pos); | 
					
						
							|  |  |  | 			grabbing = GRAB_NONE; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		queue_redraw(); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-05-20 12:38:03 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	Ref<InputEventMouseMotion> mm = p_event; | 
					
						
							|  |  |  | 	if (mm.is_valid()) { | 
					
						
							|  |  |  | 		Vector2 mpos = mm->get_position(); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 		if (grabbing != GRAB_NONE && curve.is_valid()) { | 
					
						
							|  |  |  | 			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())); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 					if (snap_enabled || mm->is_ctrl_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(); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-09-02 13:58:16 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 					// Allow to snap to axes with Shift.
 | 
					
						
							|  |  |  | 					if (mm->is_shift_pressed()) { | 
					
						
							|  |  |  | 						Vector2 initial_mpos = get_view_pos(initial_grab_pos); | 
					
						
							|  |  |  | 						if (Math::abs(mpos.x - initial_mpos.x) > Math::abs(mpos.y - initial_mpos.y)) { | 
					
						
							|  |  |  | 							new_pos.y = initial_grab_pos.y; | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							new_pos.x = initial_grab_pos.x; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 					// 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; | 
					
						
							|  |  |  | 						new_pos.x = CLAMP(new_pos.x, prev_point_offset, next_point_offset); | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 					new_pos.x = get_offset_without_collision(selected_index, new_pos.x, mpos.x >= get_view_pos(new_pos).x); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 					// The index may change if the point is dragged across another one.
 | 
					
						
							|  |  |  | 					int i = curve->set_point_offset(selected_index, new_pos.x); | 
					
						
							|  |  |  | 					hovered_index = i; | 
					
						
							|  |  |  | 					set_selected_index(i); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 					new_pos.y = CLAMP(new_pos.y, curve->get_min_value(), curve->get_max_value()); | 
					
						
							|  |  |  | 					curve->set_point_value(selected_index, new_pos.y); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 					// Drag tangent.
 | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 					const Vector2 new_pos = curve->get_point_position(selected_index); | 
					
						
							|  |  |  | 					const Vector2 control_pos = get_world_pos(mpos); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 					Vector2 dir = (control_pos - new_pos).normalized(); | 
					
						
							|  |  |  | 					real_t tangent = dir.y / (dir.x > 0 ? MAX(dir.x, 0.00001) : MIN(dir.x, -0.00001)); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 					// Must keep track of the hovered index as the cursor might move outside of the editor while dragging.
 | 
					
						
							|  |  |  | 					hovered_tangent_index = selected_tangent_index; | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 					// Adjust the tangents.
 | 
					
						
							|  |  |  | 					if (selected_tangent_index == TANGENT_LEFT) { | 
					
						
							|  |  |  | 						curve->set_point_left_tangent(selected_index, tangent); | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 						// Align the other tangent if it isn't linear and Shift is not pressed.
 | 
					
						
							|  |  |  | 						// If Shift is pressed at any point, restore the initial angle of the other tangent.
 | 
					
						
							|  |  |  | 						if (selected_index != (curve->get_point_count() - 1) && curve->get_point_right_mode(selected_index) != Curve::TANGENT_LINEAR) { | 
					
						
							|  |  |  | 							curve->set_point_right_tangent(selected_index, mm->is_shift_pressed() ? initial_grab_right_tangent : tangent); | 
					
						
							| 
									
										
										
										
											2020-05-14 16:41:43 +02:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 					} else { | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 						curve->set_point_right_tangent(selected_index, tangent); | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 						if (selected_index != 0 && curve->get_point_left_mode(selected_index) != Curve::TANGENT_LINEAR) { | 
					
						
							|  |  |  | 							curve->set_point_left_tangent(selected_index, mm->is_shift_pressed() ? initial_grab_left_tangent : tangent); | 
					
						
							| 
									
										
										
										
											2020-05-14 16:41:43 +02:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 			// Grab mode is GRAB_NONE, so do hovering logic.
 | 
					
						
							|  |  |  | 			hovered_index = get_point_at(mpos); | 
					
						
							|  |  |  | 			hovered_tangent_index = get_tangent_at(mpos); | 
					
						
							|  |  |  | 			queue_redraw(); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | void CurveEdit::use_preset(int p_preset_id) { | 
					
						
							|  |  |  | 	ERR_FAIL_COND(p_preset_id < 0 || p_preset_id >= PRESET_COUNT); | 
					
						
							|  |  |  | 	ERR_FAIL_COND(curve.is_null()); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	Array previous_data = curve->get_data(); | 
					
						
							|  |  |  | 	curve->clear_points(); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	float min_value = curve->get_min_value(); | 
					
						
							|  |  |  | 	float max_value = curve->get_max_value(); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	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->set_point_right_mode(0, Curve::TANGENT_LINEAR); | 
					
						
							|  |  |  | 			curve->set_point_left_mode(1, Curve::TANGENT_LINEAR); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 		case PRESET_LINEAR: | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 			curve->add_point(Vector2(0, min_value)); | 
					
						
							|  |  |  | 			curve->add_point(Vector2(1, max_value)); | 
					
						
							|  |  |  | 			curve->set_point_right_mode(0, Curve::TANGENT_LINEAR); | 
					
						
							|  |  |  | 			curve->set_point_left_mode(1, Curve::TANGENT_LINEAR); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 		case PRESET_EASE_IN: | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 			curve->add_point(Vector2(0, min_value)); | 
					
						
							|  |  |  | 			curve->add_point(Vector2(1, max_value), curve->get_range() * 1.4, 0); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 		case PRESET_EASE_OUT: | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 			curve->add_point(Vector2(0, min_value), 0, curve->get_range() * 1.4); | 
					
						
							|  |  |  | 			curve->add_point(Vector2(1, max_value)); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 		case PRESET_SMOOTHSTEP: | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 			curve->add_point(Vector2(0, min_value)); | 
					
						
							|  |  |  | 			curve->add_point(Vector2(1, max_value)); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 		default: | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-23 23:53:16 +01:00
										 |  |  | 	EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); | 
					
						
							| 
									
										
										
										
											2022-08-29 12:10:32 +02:00
										 |  |  | 	undo_redo->create_action(TTR("Load Curve Preset")); | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	undo_redo->add_do_method(*curve, "_set_data", curve->get_data()); | 
					
						
							|  |  |  | 	undo_redo->add_do_method(this, "set_selected_index", -1); | 
					
						
							|  |  |  | 	undo_redo->add_undo_method(*curve, "_set_data", previous_data); | 
					
						
							|  |  |  | 	undo_redo->add_undo_method(this, "set_selected_index", selected_index); | 
					
						
							| 
									
										
										
										
											2022-08-29 12:10:32 +02:00
										 |  |  | 	undo_redo->commit_action(); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | void CurveEdit::_curve_changed() { | 
					
						
							| 
									
										
										
										
											2022-08-13 23:21:24 +02:00
										 |  |  | 	queue_redraw(); | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	// Point count can change in case of undo.
 | 
					
						
							|  |  |  | 	if (selected_index >= curve->get_point_count()) { | 
					
						
							|  |  |  | 		set_selected_index(-1); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | int CurveEdit::get_point_at(Vector2 p_pos) const { | 
					
						
							|  |  |  | 	if (curve.is_null()) { | 
					
						
							|  |  |  | 		return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	// Use a square-shaped hover region. If hovering multiple points, pick the closer one.
 | 
					
						
							|  |  |  | 	const Rect2 hover_rect = Rect2(p_pos, Vector2(0, 0)).grow(hover_radius); | 
					
						
							|  |  |  | 	int closest_idx = -1; | 
					
						
							|  |  |  | 	float closest_dist_squared = hover_radius * hover_radius * 2; | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	for (int i = 0; i < curve->get_point_count(); ++i) { | 
					
						
							|  |  |  | 		Vector2 p = get_view_pos(curve->get_point_position(i)); | 
					
						
							|  |  |  | 		if (hover_rect.has_point(p) && p.distance_squared_to(p_pos) < closest_dist_squared) { | 
					
						
							|  |  |  | 			closest_dist_squared = p.distance_squared_to(p_pos); | 
					
						
							|  |  |  | 			closest_idx = i; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	return closest_idx; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | CurveEdit::TangentIndex CurveEdit::get_tangent_at(Vector2 p_pos) const { | 
					
						
							|  |  |  | 	if (curve.is_null() || selected_index < 0) { | 
					
						
							|  |  |  | 		return TANGENT_NONE; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	const Rect2 hover_rect = Rect2(p_pos, Vector2(0, 0)).grow(tangent_hover_radius); | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	if (selected_index != 0) { | 
					
						
							|  |  |  | 		Vector2 control_pos = get_tangent_view_pos(selected_index, TANGENT_LEFT); | 
					
						
							|  |  |  | 		if (hover_rect.has_point(control_pos)) { | 
					
						
							|  |  |  | 			return TANGENT_LEFT; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	if (selected_index != curve->get_point_count() - 1) { | 
					
						
							|  |  |  | 		Vector2 control_pos = get_tangent_view_pos(selected_index, TANGENT_RIGHT); | 
					
						
							|  |  |  | 		if (hover_rect.has_point(control_pos)) { | 
					
						
							|  |  |  | 			return TANGENT_RIGHT; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	return TANGENT_NONE; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | // FIXME: This function should be bounded better.
 | 
					
						
							|  |  |  | float CurveEdit::get_offset_without_collision(int p_current_index, float p_offset, bool p_prioritize_right) { | 
					
						
							|  |  |  | 	float safe_offset = p_offset; | 
					
						
							|  |  |  | 	bool prioritizing_right = p_prioritize_right; | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	for (int i = 0; i < curve->get_point_count(); i++) { | 
					
						
							|  |  |  | 		if (i == p_current_index) { | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 		if (curve->get_point_position(i).x > safe_offset) { | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 		if (curve->get_point_position(i).x == safe_offset) { | 
					
						
							|  |  |  | 			if (prioritizing_right) { | 
					
						
							|  |  |  | 				safe_offset += 0.00001; | 
					
						
							|  |  |  | 				if (safe_offset > 1.0) { | 
					
						
							|  |  |  | 					safe_offset = 1.0; | 
					
						
							|  |  |  | 					prioritizing_right = false; | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				safe_offset -= 0.00001; | 
					
						
							|  |  |  | 				if (safe_offset < 0.0) { | 
					
						
							|  |  |  | 					safe_offset = 0.0; | 
					
						
							|  |  |  | 					prioritizing_right = true; | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 			i = -1; | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	return safe_offset; | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | void CurveEdit::add_point(Vector2 p_pos) { | 
					
						
							|  |  |  | 	ERR_FAIL_COND(curve.is_null()); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	// Add a point to get its index, then remove it immediately. Trick to feed the UndoRedo.
 | 
					
						
							|  |  |  | 	int new_idx = curve->add_point(p_pos); | 
					
						
							|  |  |  | 	curve->remove_point(new_idx); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); | 
					
						
							|  |  |  | 	undo_redo->create_action(TTR("Add Curve Point")); | 
					
						
							|  |  |  | 	undo_redo->add_do_method(*curve, "add_point", p_pos); | 
					
						
							|  |  |  | 	undo_redo->add_do_method(this, "set_selected_index", new_idx); | 
					
						
							|  |  |  | 	undo_redo->add_undo_method(*curve, "remove_point", new_idx); | 
					
						
							|  |  |  | 	undo_redo->add_undo_method(this, "set_selected_index", -1); | 
					
						
							|  |  |  | 	undo_redo->commit_action(); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | void CurveEdit::remove_point(int p_index) { | 
					
						
							|  |  |  | 	ERR_FAIL_COND(curve.is_null()); | 
					
						
							|  |  |  | 	ERR_FAIL_INDEX_MSG(p_index, curve->get_point_count(), "Curve point is out of bounds."); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	Curve::Point p = curve->get_point(p_index); | 
					
						
							|  |  |  | 	Vector2 old_pos = (grabbing == GRAB_MOVE) ? initial_grab_pos : p.position; | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	int new_selected_index = selected_index; | 
					
						
							|  |  |  | 	// Reselect the old selected point if it's not the deleted one.
 | 
					
						
							|  |  |  | 	if (new_selected_index > p_index) { | 
					
						
							|  |  |  | 		new_selected_index -= 1; | 
					
						
							|  |  |  | 	} else if (new_selected_index == p_index) { | 
					
						
							|  |  |  | 		new_selected_index = -1; | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-23 23:53:16 +01:00
										 |  |  | 	EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); | 
					
						
							| 
									
										
										
										
											2022-08-29 12:10:32 +02:00
										 |  |  | 	undo_redo->create_action(TTR("Remove Curve Point")); | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	undo_redo->add_do_method(*curve, "remove_point", p_index); | 
					
						
							|  |  |  | 	undo_redo->add_do_method(this, "set_selected_index", new_selected_index); | 
					
						
							|  |  |  | 	undo_redo->add_undo_method(*curve, "add_point", old_pos, p.left_tangent, p.right_tangent, p.left_mode, p.right_mode); | 
					
						
							|  |  |  | 	undo_redo->add_undo_method(this, "set_selected_index", selected_index); | 
					
						
							|  |  |  | 	undo_redo->commit_action(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | void CurveEdit::set_point_position(int p_index, 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."); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	if (initial_grab_pos == p_pos) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-28 02:42:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	// Pretend the point started from its old place.
 | 
					
						
							|  |  |  | 	curve->set_point_value(p_index, initial_grab_pos.y); | 
					
						
							|  |  |  | 	curve->set_point_offset(p_index, initial_grab_pos.x); | 
					
						
							|  |  |  | 	// Note: Changing the offset may modify the order.
 | 
					
						
							|  |  |  | 	EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); | 
					
						
							|  |  |  | 	undo_redo->create_action(TTR("Modify Curve Point")); | 
					
						
							|  |  |  | 	undo_redo->add_do_method(*curve, "set_point_value", initial_grab_index, p_pos.y); | 
					
						
							|  |  |  | 	undo_redo->add_do_method(*curve, "set_point_offset", initial_grab_index, p_pos.x); | 
					
						
							|  |  |  | 	undo_redo->add_do_method(this, "set_selected_index", p_index); | 
					
						
							|  |  |  | 	undo_redo->add_undo_method(*curve, "set_point_value", p_index, initial_grab_pos.y); | 
					
						
							|  |  |  | 	undo_redo->add_undo_method(*curve, "set_point_offset", p_index, initial_grab_pos.x); | 
					
						
							|  |  |  | 	undo_redo->add_undo_method(this, "set_selected_index", initial_grab_index); | 
					
						
							| 
									
										
										
										
											2022-08-29 12:10:32 +02:00
										 |  |  | 	undo_redo->commit_action(); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | void CurveEdit::set_point_tangents(int p_index, float p_left, float p_right) { | 
					
						
							|  |  |  | 	ERR_FAIL_COND(curve.is_null()); | 
					
						
							|  |  |  | 	ERR_FAIL_INDEX_MSG(p_index, curve->get_point_count(), "Curve point is out of bounds."); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	if (initial_grab_left_tangent == p_left) { | 
					
						
							|  |  |  | 		set_point_right_tangent(p_index, p_right); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} else if (initial_grab_right_tangent == p_right) { | 
					
						
							|  |  |  | 		set_point_left_tangent(p_index, p_left); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	curve->set_point_left_tangent(p_index, initial_grab_left_tangent); | 
					
						
							|  |  |  | 	curve->set_point_right_tangent(p_index, initial_grab_right_tangent); | 
					
						
							|  |  |  | 	EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); | 
					
						
							|  |  |  | 	undo_redo->create_action(TTR("Modify Curve Point's Tangents")); | 
					
						
							|  |  |  | 	undo_redo->add_do_method(*curve, "set_point_left_tangent", p_index, p_left); | 
					
						
							|  |  |  | 	undo_redo->add_do_method(*curve, "set_point_right_tangent", p_index, p_right); | 
					
						
							|  |  |  | 	undo_redo->add_do_method(this, "set_selected_index", p_index); | 
					
						
							|  |  |  | 	undo_redo->add_undo_method(*curve, "set_point_left_tangent", p_index, initial_grab_left_tangent); | 
					
						
							|  |  |  | 	undo_redo->add_undo_method(*curve, "set_point_right_tangent", p_index, initial_grab_right_tangent); | 
					
						
							|  |  |  | 	undo_redo->add_undo_method(this, "set_selected_index", p_index); | 
					
						
							|  |  |  | 	undo_redo->commit_action(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | void CurveEdit::set_point_left_tangent(int p_index, float p_tangent) { | 
					
						
							|  |  |  | 	ERR_FAIL_COND(curve.is_null()); | 
					
						
							|  |  |  | 	ERR_FAIL_INDEX_MSG(p_index, curve->get_point_count(), "Curve point is out of bounds."); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	if (initial_grab_left_tangent == p_tangent) { | 
					
						
							|  |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2020-05-14 16:41:43 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-09-25 18:56:59 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	curve->set_point_left_tangent(p_index, initial_grab_left_tangent); | 
					
						
							|  |  |  | 	EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); | 
					
						
							|  |  |  | 	undo_redo->create_action(TTR("Modify Curve Point's Left Tangent")); | 
					
						
							|  |  |  | 	undo_redo->add_do_method(*curve, "set_point_left_tangent", p_index, p_tangent); | 
					
						
							|  |  |  | 	undo_redo->add_do_method(this, "set_selected_index", p_index); | 
					
						
							|  |  |  | 	undo_redo->add_undo_method(*curve, "set_point_left_tangent", p_index, initial_grab_left_tangent); | 
					
						
							|  |  |  | 	undo_redo->add_undo_method(this, "set_selected_index", p_index); | 
					
						
							| 
									
										
										
										
											2022-08-29 12:10:32 +02:00
										 |  |  | 	undo_redo->commit_action(); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | void CurveEdit::set_point_right_tangent(int p_index, float p_tangent) { | 
					
						
							|  |  |  | 	ERR_FAIL_COND(curve.is_null()); | 
					
						
							|  |  |  | 	ERR_FAIL_INDEX_MSG(p_index, curve->get_point_count(), "Curve point is out of bounds."); | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	if (initial_grab_right_tangent == p_tangent) { | 
					
						
							|  |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2020-05-14 16:41:43 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	curve->set_point_right_tangent(p_index, initial_grab_right_tangent); | 
					
						
							|  |  |  | 	EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); | 
					
						
							|  |  |  | 	undo_redo->create_action(TTR("Modify Curve Point's Right Tangent")); | 
					
						
							|  |  |  | 	undo_redo->add_do_method(*curve, "set_point_right_tangent", p_index, p_tangent); | 
					
						
							|  |  |  | 	undo_redo->add_do_method(this, "set_selected_index", p_index); | 
					
						
							|  |  |  | 	undo_redo->add_undo_method(*curve, "set_point_right_tangent", p_index, initial_grab_right_tangent); | 
					
						
							|  |  |  | 	undo_redo->add_undo_method(this, "set_selected_index", p_index); | 
					
						
							|  |  |  | 	undo_redo->commit_action(); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-06-28 02:42:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | void CurveEdit::toggle_linear(int p_index, TangentIndex p_tangent) { | 
					
						
							|  |  |  | 	ERR_FAIL_COND(curve.is_null()); | 
					
						
							|  |  |  | 	ERR_FAIL_INDEX_MSG(p_index, curve->get_point_count(), "Curve point is out of bounds."); | 
					
						
							| 
									
										
										
										
											2017-06-28 02:42:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	if (p_tangent == TANGENT_NONE) { | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-28 02:42:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); | 
					
						
							|  |  |  | 	undo_redo->create_action(TTR("Toggle Linear Curve Point's Tangent")); | 
					
						
							| 
									
										
										
										
											2017-06-28 02:42:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	Curve::TangentMode prev_mode = (p_tangent == TANGENT_LEFT) ? curve->get_point_left_mode(p_index) : curve->get_point_right_mode(p_index); | 
					
						
							|  |  |  | 	Curve::TangentMode mode = (prev_mode == Curve::TANGENT_LINEAR) ? Curve::TANGENT_FREE : Curve::TANGENT_LINEAR; | 
					
						
							|  |  |  | 	float prev_angle = (p_tangent == TANGENT_LEFT) ? curve->get_point_left_tangent(p_index) : curve->get_point_right_tangent(p_index); | 
					
						
							| 
									
										
										
										
											2017-06-28 02:42:38 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	// Add different methods in the UndoRedo based on the tangent passed.
 | 
					
						
							|  |  |  | 	if (p_tangent == TANGENT_LEFT) { | 
					
						
							|  |  |  | 		undo_redo->add_do_method(*curve, "set_point_left_mode", p_index, mode); | 
					
						
							|  |  |  | 		undo_redo->add_undo_method(*curve, "set_point_left_mode", p_index, prev_mode); | 
					
						
							|  |  |  | 		undo_redo->add_undo_method(*curve, "set_point_left_tangent", p_index, prev_angle); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		undo_redo->add_do_method(*curve, "set_point_right_mode", p_index, mode); | 
					
						
							|  |  |  | 		undo_redo->add_undo_method(*curve, "set_point_right_mode", p_index, prev_mode); | 
					
						
							|  |  |  | 		undo_redo->add_undo_method(*curve, "set_point_right_tangent", p_index, prev_angle); | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-29 12:10:32 +02:00
										 |  |  | 	undo_redo->commit_action(); | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | void CurveEdit::set_selected_index(int p_index) { | 
					
						
							|  |  |  | 	if (p_index != selected_index) { | 
					
						
							|  |  |  | 		selected_index = p_index; | 
					
						
							| 
									
										
										
										
											2022-08-13 23:21:24 +02:00
										 |  |  | 		queue_redraw(); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | void CurveEdit::update_view_transform() { | 
					
						
							| 
									
										
										
										
											2021-07-17 18:22:52 -03:00
										 |  |  | 	Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label")); | 
					
						
							|  |  |  | 	int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label")); | 
					
						
							| 
									
										
										
										
											2020-09-03 14:22:16 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	const real_t margin = font->get_height(font_size) + 2 * EDSCALE; | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	float min_y = curve.is_valid() ? curve->get_min_value() : 0.0; | 
					
						
							|  |  |  | 	float max_y = curve.is_valid() ? curve->get_max_value() : 1.0; | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-19 10:39:11 +08:00
										 |  |  | 	const Rect2 world_rect = Rect2(Curve::MIN_X, min_y, Curve::MAX_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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Transform2D world_trans; | 
					
						
							| 
									
										
										
										
											2022-07-16 11:47:54 +02:00
										 |  |  | 	world_trans.translate_local(-world_rect.position - Vector2(0, world_rect.size.y)); | 
					
						
							| 
									
										
										
										
											2019-12-19 10:39:11 +08:00
										 |  |  | 	world_trans.scale(Vector2(scale.x, -scale.y)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Transform2D view_trans; | 
					
						
							| 
									
										
										
										
											2022-07-16 11:47:54 +02:00
										 |  |  | 	view_trans.translate_local(view_margin); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-19 10:39:11 +08:00
										 |  |  | 	_world_to_view = view_trans * world_trans; | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | Vector2 CurveEdit::get_tangent_view_pos(int p_index, TangentIndex p_tangent) const { | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 	Vector2 dir; | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	if (p_tangent == TANGENT_LEFT) { | 
					
						
							|  |  |  | 		dir = -Vector2(1, curve->get_point_left_tangent(p_index)); | 
					
						
							| 
									
										
										
										
											2020-05-14 16:41:43 +02:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 		dir = Vector2(1, curve->get_point_right_tangent(p_index)); | 
					
						
							| 
									
										
										
										
											2020-05-14 16:41:43 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	Vector2 point_pos = curve->get_point_position(p_index); | 
					
						
							|  |  |  | 	Vector2 point_view_pos = get_view_pos(point_pos); | 
					
						
							|  |  |  | 	Vector2 control_view_pos = get_view_pos(point_pos + dir); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	Vector2 distance_from_point = tangent_length * (control_view_pos - point_view_pos).normalized(); | 
					
						
							|  |  |  | 	Vector2 tangent_view_pos = point_view_pos + distance_from_point; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Since the tangent is long, it might slip outside of the area of the editor for points close to the domain/range boundaries.
 | 
					
						
							|  |  |  | 	// The code below shrinks the tangent control by up to 50% so it always stays inside the editor for points within the bounds.
 | 
					
						
							|  |  |  | 	float fraction_inside = 1.0; | 
					
						
							|  |  |  | 	if (distance_from_point.x != 0.0) { | 
					
						
							|  |  |  | 		fraction_inside = MIN(fraction_inside, ((distance_from_point.x > 0 ? get_rect().size.x : 0) - point_view_pos.x) / distance_from_point.x); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (distance_from_point.y != 0.0) { | 
					
						
							|  |  |  | 		fraction_inside = MIN(fraction_inside, ((distance_from_point.y > 0 ? get_rect().size.y : 0) - point_view_pos.y) / distance_from_point.y); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (fraction_inside < 1.0 && fraction_inside > 0.5) { | 
					
						
							|  |  |  | 		tangent_view_pos = point_view_pos + distance_from_point * fraction_inside; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return tangent_view_pos; | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | Vector2 CurveEdit::get_view_pos(Vector2 p_world_pos) const { | 
					
						
							|  |  |  | 	return _world_to_view.xform(p_world_pos); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | Vector2 CurveEdit::get_world_pos(Vector2 p_view_pos) const { | 
					
						
							|  |  |  | 	return _world_to_view.affine_inverse().xform(p_view_pos); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | // Uses non-baked points, but takes advantage of ordered iteration to be faster.
 | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | template <typename T> | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | static void plot_curve_accurate(const Curve &curve, float step, Vector2 scaling, T plot_func) { | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 	if (curve.get_point_count() <= 1) { | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 		// 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.
 | 
					
						
							| 
									
										
										
										
											2022-07-24 18:47:57 +02:00
										 |  |  | 		float y = curve.sample(0); | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 		plot_func(Vector2(0, y) * scaling + Vector2(0.5, 0), Vector2(1.f, y) * scaling - Vector2(1.5, 0), true); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2017-09-10 15:37:49 +02:00
										 |  |  | 		Vector2 first_point = curve.get_point_position(0); | 
					
						
							|  |  |  | 		Vector2 last_point = curve.get_point_position(curve.get_point_count() - 1); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Edge lines
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 		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); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// 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) { | 
					
						
							| 
									
										
										
										
											2017-09-10 15:37:49 +02:00
										 |  |  | 			Vector2 a = curve.get_point_position(i - 1); | 
					
						
							|  |  |  | 			Vector2 b = curve.get_point_position(i); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			Vector2 pos = a; | 
					
						
							|  |  |  | 			Vector2 prev_pos = a; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 			float scaled_step = step / scaling.x; | 
					
						
							|  |  |  | 			float samples = (b.x - a.x) / scaled_step; | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 			for (int j = 1; j < samples; j++) { | 
					
						
							|  |  |  | 				float x = j * scaled_step; | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 				pos.x = a.x + x; | 
					
						
							| 
									
										
										
										
											2022-07-24 18:47:57 +02:00
										 |  |  | 				pos.y = curve.sample_local_nocheck(i - 1, x); | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 				plot_func(prev_pos * scaling, pos * scaling, true); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 				prev_pos = pos; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 			plot_func(prev_pos * scaling, b * scaling, true); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | struct CanvasItemPlotCurve { | 
					
						
							|  |  |  | 	CanvasItem &ci; | 
					
						
							|  |  |  | 	Color color1; | 
					
						
							|  |  |  | 	Color color2; | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-06 21:36:34 +01:00
										 |  |  | 	CanvasItemPlotCurve(CanvasItem &p_ci, Color p_color1, Color p_color2) : | 
					
						
							|  |  |  | 			ci(p_ci), | 
					
						
							|  |  |  | 			color1(p_color1), | 
					
						
							|  |  |  | 			color2(p_color2) {} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 	void operator()(Vector2 pos0, Vector2 pos1, bool in_definition) { | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 		ci.draw_line(pos0, pos1, in_definition ? color1 : color2, 0.5, true); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | void CurveEdit::_redraw() { | 
					
						
							|  |  |  | 	if (curve.is_null()) { | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 		return; | 
					
						
							| 
									
										
										
										
											2020-05-14 16:41:43 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	update_view_transform(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	// Draw background.
 | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	Vector2 view_size = get_rect().size; | 
					
						
							| 
									
										
										
										
											2022-09-06 20:09:32 +03:00
										 |  |  | 	draw_style_box(get_theme_stylebox(SNAME("panel"), SNAME("Tree")), Rect2(Point2(), view_size)); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	// Draw snapping grid, then primary grid.
 | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 	draw_set_transform_matrix(_world_to_view); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Vector2 min_edge = get_world_pos(Vector2(0, view_size.y)); | 
					
						
							|  |  |  | 	Vector2 max_edge = get_world_pos(Vector2(view_size.x, 0)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	const Color grid_color_primary = get_theme_color(SNAME("mono_color"), SNAME("Editor")) * Color(1, 1, 1, 0.25); | 
					
						
							|  |  |  | 	const Color grid_color = get_theme_color(SNAME("mono_color"), SNAME("Editor")) * Color(1, 1, 1, 0.1); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	const Vector2i grid_steps = Vector2i(4, 2); | 
					
						
							|  |  |  | 	const Vector2 step_size = Vector2(1, curve->get_range()) / grid_steps; | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	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); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (int i = 1; i < grid_steps.x; i++) { | 
					
						
							|  |  |  | 		real_t x = i * step_size.x; | 
					
						
							|  |  |  | 		draw_line(Vector2(x, min_edge.y), Vector2(x, max_edge.y), grid_color); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	for (int i = 1; i < grid_steps.y; i++) { | 
					
						
							|  |  |  | 		real_t y = curve->get_min_value() + i * step_size.y; | 
					
						
							|  |  |  | 		draw_line(Vector2(min_edge.x, y), Vector2(max_edge.x, y), grid_color); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	// Draw number markings.
 | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 	draw_set_transform_matrix(Transform2D()); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-17 18:22:52 -03:00
										 |  |  | 	Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label")); | 
					
						
							|  |  |  | 	int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label")); | 
					
						
							| 
									
										
										
										
											2020-09-03 14:22:16 +03:00
										 |  |  | 	float font_height = font->get_height(font_size); | 
					
						
							| 
									
										
										
										
											2021-07-17 18:22:52 -03:00
										 |  |  | 	Color text_color = get_theme_color(SNAME("font_color"), SNAME("Editor")); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	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); | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	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); | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	// Draw curve.
 | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	// 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)))); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	const Color line_color = get_theme_color(SNAME("font_color"), SNAME("Editor")); | 
					
						
							|  |  |  | 	const Color edge_line_color = get_theme_color(SNAME("font_color"), SNAME("Editor")) * Color(1, 1, 1, 0.75); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	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); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Draw points, except for the selected one.
 | 
					
						
							|  |  |  | 	draw_set_transform_matrix(Transform2D()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bool shift_pressed = Input::get_singleton()->is_key_pressed(Key::SHIFT); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const Color point_color = get_theme_color(SNAME("font_color"), SNAME("Editor")); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	for (int i = 0; i < curve->get_point_count(); ++i) { | 
					
						
							|  |  |  | 		Vector2 pos = get_view_pos(curve->get_point_position(i)); | 
					
						
							|  |  |  | 		if (selected_index != i) { | 
					
						
							|  |  |  | 			draw_rect(Rect2(pos, Vector2(0, 0)).grow(point_radius), point_color); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (hovered_index == i && hovered_tangent_index == TANGENT_NONE) { | 
					
						
							|  |  |  | 			draw_rect(Rect2(pos, Vector2(0, 0)).grow(hover_radius - Math::round(3 * EDSCALE)), line_color, false, Math::round(1 * EDSCALE)); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	// Draw selected point and its tangents.
 | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	if (selected_index >= 0) { | 
					
						
							|  |  |  | 		const Vector2 point_pos = curve->get_point_position(selected_index); | 
					
						
							|  |  |  | 		const Color selected_point_color = get_theme_color(SNAME("accent_color"), SNAME("Editor")); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 		// Draw tangents if not dragging a point, or if holding a point without having moved it yet.
 | 
					
						
							|  |  |  | 		if (grabbing == GRAB_NONE || (grabbing != GRAB_NONE && (initial_grab_pos == point_pos || selected_tangent_index != TANGENT_NONE))) { | 
					
						
							|  |  |  | 			const Color selected_tangent_color = get_theme_color(SNAME("accent_color"), SNAME("Editor")).darkened(0.25); | 
					
						
							|  |  |  | 			const Color tangent_color = get_theme_color(SNAME("font_color"), SNAME("Editor")).darkened(0.25); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 			if (selected_index != 0) { | 
					
						
							|  |  |  | 				Vector2 control_pos = get_tangent_view_pos(selected_index, TANGENT_LEFT); | 
					
						
							|  |  |  | 				Color left_tangent_color = (selected_tangent_index == TANGENT_LEFT) ? selected_tangent_color : tangent_color; | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 				draw_line(get_view_pos(point_pos), control_pos, left_tangent_color, 0.5 * EDSCALE, true); | 
					
						
							|  |  |  | 				// Square for linear mode, circle otherwise.
 | 
					
						
							|  |  |  | 				if (curve->get_point_left_mode(selected_index) == Curve::TANGENT_FREE) { | 
					
						
							|  |  |  | 					draw_circle(control_pos, tangent_radius, left_tangent_color); | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					draw_rect(Rect2(control_pos, Vector2(0, 0)).grow(tangent_radius), left_tangent_color); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				// Hover indicator.
 | 
					
						
							|  |  |  | 				if (hovered_tangent_index == TANGENT_LEFT || (hovered_tangent_index == TANGENT_RIGHT && !shift_pressed && curve->get_point_left_mode(selected_index) != Curve::TANGENT_LINEAR)) { | 
					
						
							|  |  |  | 					draw_rect(Rect2(control_pos, Vector2(0, 0)).grow(tangent_hover_radius - Math::round(3 * EDSCALE)), tangent_color, false, Math::round(1 * EDSCALE)); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 			if (selected_index != curve->get_point_count() - 1) { | 
					
						
							|  |  |  | 				Vector2 control_pos = get_tangent_view_pos(selected_index, TANGENT_RIGHT); | 
					
						
							|  |  |  | 				Color right_tangent_color = (selected_tangent_index == TANGENT_RIGHT) ? selected_tangent_color : tangent_color; | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 				draw_line(get_view_pos(point_pos), control_pos, right_tangent_color, 0.5 * EDSCALE, true); | 
					
						
							|  |  |  | 				// Square for linear mode, circle otherwise.
 | 
					
						
							|  |  |  | 				if (curve->get_point_right_mode(selected_index) == Curve::TANGENT_FREE) { | 
					
						
							|  |  |  | 					draw_circle(control_pos, tangent_radius, right_tangent_color); | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					draw_rect(Rect2(control_pos, Vector2(0, 0)).grow(tangent_radius), right_tangent_color); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				// Hover indicator.
 | 
					
						
							|  |  |  | 				if (hovered_tangent_index == TANGENT_RIGHT || (hovered_tangent_index == TANGENT_LEFT && !shift_pressed && curve->get_point_right_mode(selected_index) != Curve::TANGENT_LINEAR)) { | 
					
						
							|  |  |  | 					draw_rect(Rect2(control_pos, Vector2(0, 0)).grow(tangent_hover_radius - Math::round(3 * EDSCALE)), tangent_color, false, Math::round(1 * EDSCALE)); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 		draw_rect(Rect2(get_view_pos(point_pos), Vector2(0, 0)).grow(point_radius), selected_point_color); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	// Draw help text.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (selected_index > 0 && selected_index < curve->get_point_count() - 1 && selected_tangent_index == TANGENT_NONE && hovered_tangent_index != TANGENT_NONE && !shift_pressed) { | 
					
						
							|  |  |  | 		float width = view_size.x - 50 * EDSCALE; | 
					
						
							|  |  |  | 		text_color.a *= 0.4; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		draw_multiline_string(font, Vector2(25 * EDSCALE, font_height - Math::round(2 * EDSCALE)), TTR("Hold Shift to edit tangents individually"), HORIZONTAL_ALIGNMENT_CENTER, width, font_size, -1, text_color); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	} else if (selected_index != -1 && selected_tangent_index == TANGENT_NONE) { | 
					
						
							|  |  |  | 		const Vector2 point_pos = curve->get_point_position(selected_index); | 
					
						
							|  |  |  | 		float width = view_size.x - 50 * EDSCALE; | 
					
						
							|  |  |  | 		text_color.a *= 0.8; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		draw_string(font, Vector2(25 * EDSCALE, font_height - Math::round(2 * EDSCALE)), vformat("(%.2f, %.2f)", point_pos.x, point_pos.y), HORIZONTAL_ALIGNMENT_CENTER, width, font_size, text_color); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} else if (selected_index != -1 && selected_tangent_index != TANGENT_NONE) { | 
					
						
							|  |  |  | 		float width = view_size.x - 50 * EDSCALE; | 
					
						
							|  |  |  | 		text_color.a *= 0.8; | 
					
						
							|  |  |  | 		real_t theta = Math::rad_to_deg(Math::atan(selected_tangent_index == TANGENT_LEFT ? -1 * curve->get_point_left_tangent(selected_index) : curve->get_point_right_tangent(selected_index))); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		draw_string(font, Vector2(25 * EDSCALE, font_height - Math::round(2 * EDSCALE)), String::num(theta, 1) + String::utf8(" °"), HORIZONTAL_ALIGNMENT_CENTER, width, font_size, text_color); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	// Draw temporary constraints and snapping axes.
 | 
					
						
							|  |  |  | 	draw_set_transform_matrix(_world_to_view); | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	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; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		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)); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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"), SNAME("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"), SNAME("Editor")).darkened(0.4)); | 
					
						
							| 
									
										
										
										
											2017-06-26 23:39:35 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | ///////////////////////
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const int CurveEditor::DEFAULT_SNAP = 10; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CurveEditor::_set_snap_enabled(bool p_enabled) { | 
					
						
							|  |  |  | 	curve_editor_rect->set_snap_enabled(p_enabled); | 
					
						
							|  |  |  | 	snap_count_edit->set_visible(p_enabled); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CurveEditor::_set_snap_count(int p_snap_count) { | 
					
						
							|  |  |  | 	curve_editor_rect->set_snap_count(CLAMP(p_snap_count, 2, 100)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CurveEditor::_on_preset_item_selected(int p_preset_id) { | 
					
						
							|  |  |  | 	curve_editor_rect->use_preset(p_preset_id); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CurveEditor::set_curve(const Ref<Curve> &p_curve) { | 
					
						
							|  |  |  | 	curve_editor_rect->set_curve(p_curve); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void CurveEditor::_notification(int p_what) { | 
					
						
							|  |  |  | 	switch (p_what) { | 
					
						
							|  |  |  | 		case NOTIFICATION_THEME_CHANGED: { | 
					
						
							|  |  |  | 			spacing = Math::round(BASE_SPACING * get_theme_default_base_scale()); | 
					
						
							|  |  |  | 			snap_button->set_icon(get_theme_icon(SNAME("SnapGrid"), SNAME("EditorIcons"))); | 
					
						
							|  |  |  | 			PopupMenu *p = presets_button->get_popup(); | 
					
						
							|  |  |  | 			p->clear(); | 
					
						
							|  |  |  | 			p->add_icon_item(get_theme_icon(SNAME("CurveConstant"), SNAME("EditorIcons")), TTR("Constant"), CurveEdit::PRESET_CONSTANT); | 
					
						
							|  |  |  | 			p->add_icon_item(get_theme_icon(SNAME("CurveLinear"), SNAME("EditorIcons")), TTR("Linear"), CurveEdit::PRESET_LINEAR); | 
					
						
							|  |  |  | 			p->add_icon_item(get_theme_icon(SNAME("CurveIn"), SNAME("EditorIcons")), TTR("Ease In"), CurveEdit::PRESET_EASE_IN); | 
					
						
							|  |  |  | 			p->add_icon_item(get_theme_icon(SNAME("CurveOut"), SNAME("EditorIcons")), TTR("Ease Out"), CurveEdit::PRESET_EASE_OUT); | 
					
						
							|  |  |  | 			p->add_icon_item(get_theme_icon(SNAME("CurveInOut"), SNAME("EditorIcons")), TTR("Smoothstep"), CurveEdit::PRESET_SMOOTHSTEP); | 
					
						
							|  |  |  | 		} break; | 
					
						
							|  |  |  | 		case NOTIFICATION_READY: { | 
					
						
							|  |  |  | 			Ref<Curve> curve = curve_editor_rect->get_curve(); | 
					
						
							|  |  |  | 			// Set snapping settings based on the curve's meta.
 | 
					
						
							|  |  |  | 			snap_button->set_pressed(curve->get_meta("_snap_enabled", false)); | 
					
						
							|  |  |  | 			snap_count_edit->set_value(curve->get_meta("_snap_count", DEFAULT_SNAP)); | 
					
						
							|  |  |  | 		} break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | CurveEditor::CurveEditor() { | 
					
						
							|  |  |  | 	HFlowContainer *toolbar = memnew(HFlowContainer); | 
					
						
							|  |  |  | 	add_child(toolbar); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	snap_button = memnew(Button); | 
					
						
							|  |  |  | 	snap_button->set_tooltip_text(TTR("Toggle Grid Snap")); | 
					
						
							|  |  |  | 	snap_button->set_toggle_mode(true); | 
					
						
							|  |  |  | 	toolbar->add_child(snap_button); | 
					
						
							|  |  |  | 	snap_button->connect("toggled", callable_mp(this, &CurveEditor::_set_snap_enabled)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	toolbar->add_child(memnew(VSeparator)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	snap_count_edit = memnew(EditorSpinSlider); | 
					
						
							|  |  |  | 	snap_count_edit->set_min(2); | 
					
						
							|  |  |  | 	snap_count_edit->set_max(100); | 
					
						
							|  |  |  | 	snap_count_edit->set_value(DEFAULT_SNAP); | 
					
						
							|  |  |  | 	snap_count_edit->set_custom_minimum_size(Size2(65 * EDSCALE, 0)); | 
					
						
							|  |  |  | 	toolbar->add_child(snap_count_edit); | 
					
						
							|  |  |  | 	snap_count_edit->connect("value_changed", callable_mp(this, &CurveEditor::_set_snap_count)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	presets_button = memnew(MenuButton); | 
					
						
							|  |  |  | 	presets_button->set_text(TTR("Presets")); | 
					
						
							|  |  |  | 	presets_button->set_switch_on_hover(true); | 
					
						
							|  |  |  | 	presets_button->set_h_size_flags(SIZE_EXPAND | SIZE_SHRINK_END); | 
					
						
							|  |  |  | 	toolbar->add_child(presets_button); | 
					
						
							|  |  |  | 	presets_button->get_popup()->connect("id_pressed", callable_mp(this, &CurveEditor::_on_preset_item_selected)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	curve_editor_rect = memnew(CurveEdit); | 
					
						
							|  |  |  | 	add_child(curve_editor_rect); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Some empty space below. Not a part of the curve editor so it can't draw in it.
 | 
					
						
							|  |  |  | 	Control *empty_space = memnew(Control); | 
					
						
							|  |  |  | 	empty_space->set_custom_minimum_size(Vector2(0, spacing)); | 
					
						
							|  |  |  | 	add_child(empty_space); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	set_mouse_filter(MOUSE_FILTER_STOP); | 
					
						
							|  |  |  | 	_set_snap_enabled(snap_button->is_pressed()); | 
					
						
							|  |  |  | 	_set_snap_count(snap_count_edit->get_value()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ///////////////////////
 | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-17 18:02:16 -03:00
										 |  |  | bool EditorInspectorPluginCurve::can_handle(Object *p_object) { | 
					
						
							| 
									
										
										
										
											2020-04-02 01:20:12 +02:00
										 |  |  | 	return Object::cast_to<Curve>(p_object) != nullptr; | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-17 18:02:16 -03:00
										 |  |  | void EditorInspectorPluginCurve::parse_begin(Object *p_object) { | 
					
						
							|  |  |  | 	Curve *curve = Object::cast_to<Curve>(p_object); | 
					
						
							|  |  |  | 	ERR_FAIL_COND(!curve); | 
					
						
							|  |  |  | 	Ref<Curve> c(curve); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-17 18:02:16 -03:00
										 |  |  | 	CurveEditor *editor = memnew(CurveEditor); | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	editor->set_curve(c); | 
					
						
							| 
									
										
										
										
											2018-05-17 18:02:16 -03:00
										 |  |  | 	add_custom_control(editor); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-27 10:36:51 +01:00
										 |  |  | CurveEditorPlugin::CurveEditorPlugin() { | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	Ref<EditorInspectorPluginCurve> plugin; | 
					
						
							|  |  |  | 	plugin.instantiate(); | 
					
						
							|  |  |  | 	add_inspector_plugin(plugin); | 
					
						
							| 
									
										
										
										
											2017-04-30 16:27:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-05 17:14:46 +02:00
										 |  |  | 	EditorInterface::get_singleton()->get_resource_previewer()->add_preview_generator(memnew(CurvePreviewGenerator)); | 
					
						
							| 
									
										
										
										
											2017-04-06 23:36:37 -03:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-07-02 01:46:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | ///////////////////////
 | 
					
						
							| 
									
										
										
										
											2017-07-02 01:46:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | bool CurvePreviewGenerator::handles(const String &p_type) const { | 
					
						
							|  |  |  | 	return p_type == "Curve"; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-19 18:14:57 +02:00
										 |  |  | Ref<Texture2D> CurvePreviewGenerator::generate(const Ref<Resource> &p_from, const Size2 &p_size, Dictionary &p_metadata) const { | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	Ref<Curve> curve = p_from; | 
					
						
							|  |  |  | 	if (curve.is_null()) { | 
					
						
							|  |  |  | 		return Ref<Texture2D>(); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-07-02 01:46:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	Size2 thumbnail_size = p_size * EDSCALE; | 
					
						
							| 
									
										
										
										
											2017-07-02 01:46:44 +02:00
										 |  |  | 	Ref<Image> img_ref; | 
					
						
							| 
									
										
										
										
											2021-06-17 16:03:09 -06:00
										 |  |  | 	img_ref.instantiate(); | 
					
						
							| 
									
										
										
										
											2017-07-02 01:46:44 +02:00
										 |  |  | 	Image &im = **img_ref; | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	im.initialize_data(thumbnail_size.x, thumbnail_size.y, false, Image::FORMAT_RGBA8); | 
					
						
							| 
									
										
										
										
											2017-07-02 01:46:44 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	Color bg_color(0.1, 0.1, 0.1, 1.0); | 
					
						
							|  |  |  | 	Color line_color(0.8, 0.8, 0.8, 1.0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 	im.fill(bg_color); | 
					
						
							|  |  |  | 	// Set the first pixel of the thumbnail.
 | 
					
						
							|  |  |  | 	float v = (curve->sample_baked(0) - curve->get_min_value()) / curve->get_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) { | 
					
						
							| 
									
										
										
										
											2017-07-02 01:46:44 +02:00
										 |  |  | 		float t = static_cast<float>(x) / im.get_width(); | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 		v = (curve->sample_baked(t) - curve->get_min_value()) / curve->get_range(); | 
					
						
							|  |  |  | 		y = CLAMP(im.get_height() - v * im.get_height(), 0, im.get_height() - 1); | 
					
						
							| 
									
										
										
										
											2017-07-02 01:46:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 		Vector<Point2i> points = Geometry2D::bresenham_line(Point2i(x - 1, prev_y), Point2i(x, y)); | 
					
						
							|  |  |  | 		for (Point2i point : points) { | 
					
						
							|  |  |  | 			im.set_pixelv(point, line_color); | 
					
						
							| 
									
										
										
										
											2017-07-02 01:46:44 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		prev_y = y; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-03-13 23:03:36 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-04 01:49:20 +02:00
										 |  |  | 	return ImageTexture::create_from_image(img_ref); | 
					
						
							| 
									
										
										
										
											2017-07-02 01:46:44 +02:00
										 |  |  | } |