path_2d_editor_plugin.cpp 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007
  1. /**************************************************************************/
  2. /* path_2d_editor_plugin.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #include "path_2d_editor_plugin.h"
  31. #include "core/os/keyboard.h"
  32. #include "editor/editor_node.h"
  33. #include "editor/editor_undo_redo_manager.h"
  34. #include "editor/scene/canvas_item_editor_plugin.h"
  35. #include "editor/settings/editor_settings.h"
  36. #include "editor/themes/editor_scale.h"
  37. #include "scene/gui/dialogs.h"
  38. #include "scene/gui/menu_button.h"
  39. #include "scene/resources/mesh.h"
  40. void Path2DEditor::_notification(int p_what) {
  41. switch (p_what) {
  42. case NOTIFICATION_THEME_CHANGED: {
  43. curve_edit->set_button_icon(get_editor_theme_icon(SNAME("CurveEdit")));
  44. curve_edit_curve->set_button_icon(get_editor_theme_icon(SNAME("CurveCurve")));
  45. curve_create->set_button_icon(get_editor_theme_icon(SNAME("CurveCreate")));
  46. curve_del->set_button_icon(get_editor_theme_icon(SNAME("CurveDelete")));
  47. curve_close->set_button_icon(get_editor_theme_icon(SNAME("CurveClose")));
  48. curve_clear_points->set_button_icon(get_editor_theme_icon(SNAME("Clear")));
  49. create_curve_button->set_button_icon(get_editor_theme_icon(SNAME("Curve2D")));
  50. } break;
  51. }
  52. }
  53. void Path2DEditor::_node_removed(Node *p_node) {
  54. if (p_node == node) {
  55. node = nullptr;
  56. hide();
  57. }
  58. }
  59. bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) {
  60. if (!node) {
  61. return false;
  62. }
  63. if (!node->is_visible_in_tree()) {
  64. return false;
  65. }
  66. Viewport *vp = node->get_viewport();
  67. if (vp && !vp->is_visible_subviewport()) {
  68. return false;
  69. }
  70. if (node->get_curve().is_null()) {
  71. return false;
  72. }
  73. real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
  74. Ref<InputEventMouseButton> mb = p_event;
  75. if (mb.is_valid()) {
  76. Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_screen_transform();
  77. Vector2 gpoint = mb->get_position();
  78. Vector2 cpoint = canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint));
  79. cpoint = node->to_local(node->get_viewport()->get_popup_base_transform().affine_inverse().xform(cpoint));
  80. if (mb->is_pressed() && action == ACTION_NONE) {
  81. Ref<Curve2D> curve = node->get_curve();
  82. original_mouse_pos = gpoint;
  83. for (int i = 0; i < curve->get_point_count(); i++) {
  84. real_t dist_to_p = gpoint.distance_to(xform.xform(curve->get_point_position(i)));
  85. real_t dist_to_p_out = gpoint.distance_to(xform.xform(curve->get_point_position(i) + curve->get_point_out(i)));
  86. real_t dist_to_p_in = gpoint.distance_to(xform.xform(curve->get_point_position(i) + curve->get_point_in(i)));
  87. // Check for point movement start (for point + in/out controls).
  88. if (mb->get_button_index() == MouseButton::LEFT) {
  89. if (mode == MODE_EDIT && !mb->is_shift_pressed() && dist_to_p < grab_threshold) {
  90. // Points can only be moved in edit mode.
  91. action = ACTION_MOVING_POINT;
  92. action_point = i;
  93. moving_from = curve->get_point_position(i);
  94. moving_screen_from = gpoint;
  95. return true;
  96. } else if (mode == MODE_EDIT || mode == MODE_EDIT_CURVE) {
  97. control_points_in_range = 0;
  98. // In/out controls can be moved in multiple modes.
  99. if (dist_to_p_out < grab_threshold && i < (curve->get_point_count() - 1)) {
  100. action = ACTION_MOVING_OUT;
  101. action_point = i;
  102. moving_from = curve->get_point_out(i);
  103. moving_screen_from = gpoint;
  104. orig_in_length = curve->get_point_in(action_point).length();
  105. control_points_in_range += 1;
  106. }
  107. if (dist_to_p_in < grab_threshold && i > 0) {
  108. action = ACTION_MOVING_IN;
  109. action_point = i;
  110. moving_from = curve->get_point_in(i);
  111. moving_screen_from = gpoint;
  112. orig_out_length = curve->get_point_out(action_point).length();
  113. control_points_in_range += 1;
  114. }
  115. if (control_points_in_range > 0) {
  116. return true;
  117. }
  118. }
  119. }
  120. // Check for point deletion.
  121. EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
  122. if ((mb->get_button_index() == MouseButton::RIGHT && (mode == MODE_EDIT || mode == MODE_CREATE)) || (mb->get_button_index() == MouseButton::LEFT && mode == MODE_DELETE)) {
  123. if (dist_to_p < grab_threshold) {
  124. undo_redo->create_action(TTR("Remove Point from Curve"));
  125. undo_redo->add_do_method(curve.ptr(), "remove_point", i);
  126. undo_redo->add_undo_method(curve.ptr(), "add_point", curve->get_point_position(i), curve->get_point_in(i), curve->get_point_out(i), i);
  127. undo_redo->add_do_method(canvas_item_editor, "update_viewport");
  128. undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
  129. undo_redo->commit_action();
  130. return true;
  131. } else if (dist_to_p_out < grab_threshold) {
  132. undo_redo->create_action(TTR("Remove Out-Control from Curve"));
  133. undo_redo->add_do_method(curve.ptr(), "set_point_out", i, Vector2());
  134. undo_redo->add_undo_method(curve.ptr(), "set_point_out", i, curve->get_point_out(i));
  135. undo_redo->add_do_method(canvas_item_editor, "update_viewport");
  136. undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
  137. undo_redo->commit_action();
  138. return true;
  139. } else if (dist_to_p_in < grab_threshold) {
  140. undo_redo->create_action(TTR("Remove In-Control from Curve"));
  141. undo_redo->add_do_method(curve.ptr(), "set_point_in", i, Vector2());
  142. undo_redo->add_undo_method(curve.ptr(), "set_point_in", i, curve->get_point_in(i));
  143. undo_redo->add_do_method(canvas_item_editor, "update_viewport");
  144. undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
  145. undo_redo->commit_action();
  146. return true;
  147. }
  148. }
  149. }
  150. }
  151. if (action != ACTION_NONE && mb->is_pressed() && mb->get_button_index() == MouseButton::RIGHT) {
  152. _cancel_current_action();
  153. return true;
  154. }
  155. // Check for point creation.
  156. if (mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT && ((mb->is_command_or_control_pressed() && mode == MODE_EDIT) || mode == MODE_CREATE)) {
  157. Ref<Curve2D> curve = node->get_curve();
  158. curve->add_point(cpoint);
  159. moving_from = cpoint;
  160. action = ACTION_MOVING_NEW_POINT;
  161. action_point = curve->get_point_count() - 1;
  162. moving_from = curve->get_point_position(action_point);
  163. moving_screen_from = gpoint;
  164. canvas_item_editor->update_viewport();
  165. return true;
  166. }
  167. // Check for segment split.
  168. if (mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT && mode == MODE_EDIT && on_edge) {
  169. Vector2 gpoint2 = mb->get_position();
  170. Ref<Curve2D> curve = node->get_curve();
  171. int insertion_point = -1;
  172. float mbLength = curve->get_closest_offset(xform.affine_inverse().xform(gpoint2));
  173. int len = curve->get_point_count();
  174. for (int i = 0; i < len - 1; i++) {
  175. float compareLength = curve->get_closest_offset(curve->get_point_position(i + 1));
  176. if (mbLength >= curve->get_closest_offset(curve->get_point_position(i)) && mbLength <= compareLength) {
  177. insertion_point = i;
  178. }
  179. }
  180. if (insertion_point == -1) {
  181. insertion_point = curve->get_point_count() - 2;
  182. }
  183. const Vector2 new_point = xform.affine_inverse().xform(gpoint2);
  184. curve->add_point(new_point, Vector2(0, 0), Vector2(0, 0), insertion_point + 1);
  185. action = ACTION_MOVING_NEW_POINT_FROM_SPLIT;
  186. action_point = insertion_point + 1;
  187. moving_from = curve->get_point_position(action_point);
  188. moving_screen_from = gpoint2;
  189. canvas_item_editor->update_viewport();
  190. on_edge = false;
  191. return true;
  192. }
  193. // Check for point movement completion.
  194. if (!mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT && action != ACTION_NONE) {
  195. Ref<Curve2D> curve = node->get_curve();
  196. EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
  197. Vector2 new_pos = moving_from + xform.affine_inverse().basis_xform(gpoint - moving_screen_from);
  198. switch (action) {
  199. case ACTION_NONE:
  200. // N/A, handled in above condition.
  201. break;
  202. case ACTION_MOVING_POINT:
  203. if (original_mouse_pos != gpoint) {
  204. undo_redo->create_action(TTR("Move Point in Curve"));
  205. undo_redo->add_undo_method(curve.ptr(), "set_point_position", action_point, moving_from);
  206. undo_redo->add_do_method(curve.ptr(), "set_point_position", action_point, cpoint);
  207. undo_redo->add_do_method(canvas_item_editor, "update_viewport");
  208. undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
  209. undo_redo->commit_action(false);
  210. }
  211. break;
  212. case ACTION_MOVING_NEW_POINT: {
  213. undo_redo->create_action(TTR("Add Point to Curve"));
  214. undo_redo->add_do_method(curve.ptr(), "add_point", cpoint);
  215. undo_redo->add_do_method(curve.ptr(), "set_point_position", action_point, cpoint);
  216. undo_redo->add_do_method(canvas_item_editor, "update_viewport");
  217. undo_redo->add_undo_method(curve.ptr(), "remove_point", curve->get_point_count() - 1);
  218. undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
  219. undo_redo->commit_action(false);
  220. } break;
  221. case ACTION_MOVING_NEW_POINT_FROM_SPLIT: {
  222. undo_redo->create_action(TTR("Split Curve"));
  223. undo_redo->add_do_method(curve.ptr(), "add_point", Vector2(), Vector2(), Vector2(), action_point);
  224. undo_redo->add_do_method(curve.ptr(), "set_point_position", action_point, cpoint);
  225. undo_redo->add_undo_method(curve.ptr(), "remove_point", action_point);
  226. undo_redo->add_do_method(canvas_item_editor, "update_viewport");
  227. undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
  228. undo_redo->commit_action(false);
  229. } break;
  230. case ACTION_MOVING_IN: {
  231. if (original_mouse_pos != gpoint) {
  232. undo_redo->create_action(TTR("Move In-Control in Curve"));
  233. undo_redo->add_do_method(curve.ptr(), "set_point_in", action_point, new_pos);
  234. undo_redo->add_undo_method(curve.ptr(), "set_point_in", action_point, moving_from);
  235. if (mirror_handle_angle) {
  236. undo_redo->add_do_method(curve.ptr(), "set_point_out", action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_out_length));
  237. undo_redo->add_undo_method(curve.ptr(), "set_point_out", action_point, mirror_handle_length ? -moving_from : (-moving_from.normalized() * orig_out_length));
  238. }
  239. undo_redo->add_do_method(canvas_item_editor, "update_viewport");
  240. undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
  241. undo_redo->commit_action();
  242. }
  243. } break;
  244. case ACTION_MOVING_OUT: {
  245. if (original_mouse_pos != gpoint) {
  246. undo_redo->create_action(TTR("Move Out-Control in Curve"));
  247. undo_redo->add_do_method(curve.ptr(), "set_point_out", action_point, new_pos);
  248. undo_redo->add_undo_method(curve.ptr(), "set_point_out", action_point, moving_from);
  249. if (mirror_handle_angle) {
  250. undo_redo->add_do_method(curve.ptr(), "set_point_in", action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_in_length));
  251. undo_redo->add_undo_method(curve.ptr(), "set_point_in", action_point, mirror_handle_length ? -moving_from : (-moving_from.normalized() * orig_in_length));
  252. }
  253. undo_redo->add_do_method(canvas_item_editor, "update_viewport");
  254. undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
  255. undo_redo->commit_action();
  256. }
  257. } break;
  258. }
  259. action = ACTION_NONE;
  260. return true;
  261. }
  262. }
  263. Ref<InputEventMouseMotion> mm = p_event;
  264. if (mm.is_valid()) {
  265. // When both control points were in range of click,
  266. // pick the point that drags the curve outwards.
  267. if (control_points_in_range == 2) {
  268. control_points_in_range = 0;
  269. Ref<Curve2D> curve = node->get_curve();
  270. Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_screen_transform();
  271. Point2 relative = xform.affine_inverse().basis_xform(mm->get_relative());
  272. real_t angle_in = relative.angle_to(curve->get_point_position(action_point - 1) - curve->get_point_position(action_point));
  273. real_t angle_out = relative.angle_to(curve->get_point_position(action_point + 1) - curve->get_point_position(action_point));
  274. if (Math::abs(angle_in) < Math::abs(angle_out)) {
  275. action = ACTION_MOVING_IN;
  276. moving_from = curve->get_point_in(action_point);
  277. orig_out_length = curve->get_point_out(action_point).length();
  278. } else {
  279. action = ACTION_MOVING_OUT;
  280. moving_from = curve->get_point_out(action_point);
  281. orig_in_length = curve->get_point_in(action_point).length();
  282. }
  283. }
  284. if (action == ACTION_NONE && mode == MODE_EDIT) {
  285. // Handle Edge Follow
  286. bool old_edge = on_edge;
  287. Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_screen_transform();
  288. Vector2 gpoint = mm->get_position();
  289. Ref<Curve2D> curve = node->get_curve();
  290. if (curve.is_null()) {
  291. return true;
  292. }
  293. if (curve->get_point_count() < 2) {
  294. return true;
  295. }
  296. // Find edge
  297. edge_point = xform.xform(curve->get_closest_point(xform.affine_inverse().xform(mm->get_position())));
  298. on_edge = false;
  299. if (edge_point.distance_to(gpoint) <= grab_threshold) {
  300. on_edge = true;
  301. }
  302. // However, if near a control point or its in-out handles then not on edge
  303. int len = curve->get_point_count();
  304. for (int i = 0; i < len; i++) {
  305. Vector2 pp = curve->get_point_position(i);
  306. Vector2 p = xform.xform(pp);
  307. if (p.distance_to(gpoint) <= grab_threshold) {
  308. on_edge = false;
  309. break;
  310. }
  311. p = xform.xform(pp + curve->get_point_in(i));
  312. if (p.distance_to(gpoint) <= grab_threshold) {
  313. on_edge = false;
  314. break;
  315. }
  316. p = xform.xform(pp + curve->get_point_out(i));
  317. if (p.distance_to(gpoint) <= grab_threshold) {
  318. on_edge = false;
  319. break;
  320. }
  321. }
  322. if (on_edge || old_edge != on_edge) {
  323. canvas_item_editor->update_viewport();
  324. return true;
  325. }
  326. }
  327. if (action != ACTION_NONE) {
  328. // Handle point/control movement.
  329. Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_screen_transform();
  330. Vector2 gpoint = mm->get_position();
  331. Vector2 cpoint = canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint));
  332. cpoint = node->to_local(node->get_viewport()->get_popup_base_transform().affine_inverse().xform(cpoint));
  333. Ref<Curve2D> curve = node->get_curve();
  334. Vector2 new_pos = moving_from + xform.affine_inverse().basis_xform(gpoint - moving_screen_from);
  335. switch (action) {
  336. case ACTION_NONE:
  337. // N/A, handled in above condition.
  338. break;
  339. case ACTION_MOVING_POINT:
  340. case ACTION_MOVING_NEW_POINT:
  341. case ACTION_MOVING_NEW_POINT_FROM_SPLIT: {
  342. curve->set_point_position(action_point, cpoint);
  343. } break;
  344. case ACTION_MOVING_IN: {
  345. curve->set_point_in(action_point, new_pos);
  346. if (mirror_handle_angle) {
  347. curve->set_point_out(action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_out_length));
  348. }
  349. } break;
  350. case ACTION_MOVING_OUT: {
  351. curve->set_point_out(action_point, new_pos);
  352. if (mirror_handle_angle) {
  353. curve->set_point_in(action_point, mirror_handle_length ? -new_pos : (-new_pos.normalized() * orig_in_length));
  354. }
  355. } break;
  356. }
  357. canvas_item_editor->update_viewport();
  358. return true;
  359. }
  360. }
  361. return false;
  362. }
  363. void Path2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
  364. if (!node || !node->is_visible_in_tree() || node->get_curve().is_null()) {
  365. return;
  366. }
  367. Viewport *vp = node->get_viewport();
  368. if (vp && !vp->is_visible_subviewport()) {
  369. return;
  370. }
  371. Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_screen_transform();
  372. const Ref<Texture2D> path_sharp_handle = get_editor_theme_icon(SNAME("EditorPathSharpHandle"));
  373. const Ref<Texture2D> path_smooth_handle = get_editor_theme_icon(SNAME("EditorPathSmoothHandle"));
  374. // Both handle icons must be of the same size
  375. const Size2 handle_size = path_sharp_handle->get_size();
  376. const Ref<Texture2D> curve_handle = get_editor_theme_icon(SNAME("EditorCurveHandle"));
  377. const Size2 curve_handle_size = curve_handle->get_size();
  378. Ref<Curve2D> curve = node->get_curve();
  379. int len = curve->get_point_count();
  380. Control *vpc = canvas_item_editor->get_viewport_control();
  381. debug_handle_lines.clear();
  382. debug_handle_curve_transforms.clear();
  383. debug_handle_sharp_transforms.clear();
  384. debug_handle_smooth_transforms.clear();
  385. Transform2D handle_curve_transform = Transform2D().scaled(curve_handle_size * 0.5);
  386. Transform2D handle_point_transform = Transform2D().scaled(handle_size * 0.5);
  387. for (int i = 0; i < len; i++) {
  388. Vector2 point = xform.xform(curve->get_point_position(i));
  389. // Determines the point icon to be used
  390. bool smooth = false;
  391. if (i < len - 1) {
  392. Vector2 point_out = xform.xform(curve->get_point_position(i) + curve->get_point_out(i));
  393. if (point != point_out) {
  394. smooth = true;
  395. debug_handle_lines.push_back(point);
  396. debug_handle_lines.push_back(point_out);
  397. handle_curve_transform.set_origin(point_out);
  398. debug_handle_curve_transforms.push_back(handle_curve_transform);
  399. }
  400. }
  401. if (i > 0) {
  402. Vector2 point_in = xform.xform(curve->get_point_position(i) + curve->get_point_in(i));
  403. if (point != point_in) {
  404. smooth = true;
  405. debug_handle_lines.push_back(point);
  406. debug_handle_lines.push_back(point_in);
  407. handle_curve_transform.set_origin(point_in);
  408. debug_handle_curve_transforms.push_back(handle_curve_transform);
  409. }
  410. }
  411. handle_point_transform.set_origin(point);
  412. if (smooth) {
  413. debug_handle_smooth_transforms.push_back(handle_point_transform);
  414. } else {
  415. debug_handle_sharp_transforms.push_back(handle_point_transform);
  416. }
  417. }
  418. if (on_edge) {
  419. Ref<Texture2D> add_handle = get_editor_theme_icon(SNAME("EditorHandleAdd"));
  420. p_overlay->draw_texture(add_handle, edge_point - add_handle->get_size() * 0.5);
  421. }
  422. RenderingServer *rs = RS::get_singleton();
  423. rs->mesh_clear(debug_mesh_rid);
  424. if (!debug_handle_lines.is_empty()) {
  425. Array handles_array;
  426. handles_array.resize(Mesh::ARRAY_MAX);
  427. handles_array[Mesh::ARRAY_VERTEX] = Vector<Vector2>(debug_handle_lines);
  428. rs->mesh_add_surface_from_arrays(debug_mesh_rid, RS::PRIMITIVE_LINES, handles_array, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES);
  429. rs->canvas_item_add_mesh(vpc->get_canvas_item(), debug_mesh_rid, Transform2D(), Color(0.5, 0.5, 0.5, 1.0));
  430. }
  431. // Add texture rects multimeshes for handle vertices.
  432. uint32_t handle_curve_count = debug_handle_curve_transforms.size();
  433. uint32_t handle_sharp_count = debug_handle_sharp_transforms.size();
  434. uint32_t handle_smooth_count = debug_handle_smooth_transforms.size();
  435. // Add texture rects for curve handle vertices.
  436. rs->multimesh_set_visible_instances(debug_handle_curve_multimesh_rid, 0);
  437. if (handle_curve_count > 0) {
  438. if (rs->multimesh_get_instance_count(debug_handle_curve_multimesh_rid) != int(handle_curve_count)) {
  439. rs->multimesh_allocate_data(debug_handle_curve_multimesh_rid, handle_curve_count, RS::MULTIMESH_TRANSFORM_2D);
  440. }
  441. Vector<float> multimesh_buffer;
  442. multimesh_buffer.resize(8 * handle_curve_count);
  443. float *multimesh_buffer_ptrw = multimesh_buffer.ptrw();
  444. const Transform2D *debug_handle_transforms_ptr = debug_handle_curve_transforms.ptr();
  445. for (uint32_t i = 0; i < handle_curve_count; i++) {
  446. const Transform2D &handle_transform = debug_handle_transforms_ptr[i];
  447. multimesh_buffer_ptrw[i * 8 + 0] = handle_transform[0][0];
  448. multimesh_buffer_ptrw[i * 8 + 1] = handle_transform[1][0];
  449. multimesh_buffer_ptrw[i * 8 + 2] = 0;
  450. multimesh_buffer_ptrw[i * 8 + 3] = handle_transform[2][0];
  451. multimesh_buffer_ptrw[i * 8 + 4] = handle_transform[0][1];
  452. multimesh_buffer_ptrw[i * 8 + 5] = handle_transform[1][1];
  453. multimesh_buffer_ptrw[i * 8 + 6] = 0;
  454. multimesh_buffer_ptrw[i * 8 + 7] = handle_transform[2][1];
  455. }
  456. rs->multimesh_set_buffer(debug_handle_curve_multimesh_rid, multimesh_buffer);
  457. rs->multimesh_set_visible_instances(debug_handle_curve_multimesh_rid, handle_curve_count);
  458. rs->canvas_item_add_multimesh(vpc->get_canvas_item(), debug_handle_curve_multimesh_rid, curve_handle->get_rid());
  459. }
  460. // Add texture rects for sharp handle vertices.
  461. rs->multimesh_set_visible_instances(debug_handle_sharp_multimesh_rid, 0);
  462. if (handle_sharp_count > 0) {
  463. if (rs->multimesh_get_instance_count(debug_handle_sharp_multimesh_rid) != int(handle_sharp_count)) {
  464. rs->multimesh_allocate_data(debug_handle_sharp_multimesh_rid, handle_sharp_count, RS::MULTIMESH_TRANSFORM_2D);
  465. }
  466. Vector<float> multimesh_buffer;
  467. multimesh_buffer.resize(8 * handle_sharp_count);
  468. float *multimesh_buffer_ptrw = multimesh_buffer.ptrw();
  469. const Transform2D *debug_handle_transforms_ptr = debug_handle_sharp_transforms.ptr();
  470. for (uint32_t i = 0; i < handle_sharp_count; i++) {
  471. const Transform2D &handle_transform = debug_handle_transforms_ptr[i];
  472. multimesh_buffer_ptrw[i * 8 + 0] = handle_transform[0][0];
  473. multimesh_buffer_ptrw[i * 8 + 1] = handle_transform[1][0];
  474. multimesh_buffer_ptrw[i * 8 + 2] = 0;
  475. multimesh_buffer_ptrw[i * 8 + 3] = handle_transform[2][0];
  476. multimesh_buffer_ptrw[i * 8 + 4] = handle_transform[0][1];
  477. multimesh_buffer_ptrw[i * 8 + 5] = handle_transform[1][1];
  478. multimesh_buffer_ptrw[i * 8 + 6] = 0;
  479. multimesh_buffer_ptrw[i * 8 + 7] = handle_transform[2][1];
  480. }
  481. rs->multimesh_set_buffer(debug_handle_sharp_multimesh_rid, multimesh_buffer);
  482. rs->multimesh_set_visible_instances(debug_handle_sharp_multimesh_rid, handle_sharp_count);
  483. rs->canvas_item_add_multimesh(vpc->get_canvas_item(), debug_handle_sharp_multimesh_rid, curve_handle->get_rid());
  484. }
  485. // Add texture rects for smooth handle vertices.
  486. rs->multimesh_set_visible_instances(debug_handle_smooth_multimesh_rid, 0);
  487. if (handle_smooth_count > 0) {
  488. if (rs->multimesh_get_instance_count(debug_handle_smooth_multimesh_rid) != int(handle_smooth_count)) {
  489. rs->multimesh_allocate_data(debug_handle_smooth_multimesh_rid, handle_smooth_count, RS::MULTIMESH_TRANSFORM_2D);
  490. }
  491. Vector<float> multimesh_buffer;
  492. multimesh_buffer.resize(8 * handle_smooth_count);
  493. float *multimesh_buffer_ptrw = multimesh_buffer.ptrw();
  494. const Transform2D *debug_handle_transforms_ptr = debug_handle_smooth_transforms.ptr();
  495. for (uint32_t i = 0; i < handle_smooth_count; i++) {
  496. const Transform2D &handle_transform = debug_handle_transforms_ptr[i];
  497. multimesh_buffer_ptrw[i * 8 + 0] = handle_transform[0][0];
  498. multimesh_buffer_ptrw[i * 8 + 1] = handle_transform[1][0];
  499. multimesh_buffer_ptrw[i * 8 + 2] = 0;
  500. multimesh_buffer_ptrw[i * 8 + 3] = handle_transform[2][0];
  501. multimesh_buffer_ptrw[i * 8 + 4] = handle_transform[0][1];
  502. multimesh_buffer_ptrw[i * 8 + 5] = handle_transform[1][1];
  503. multimesh_buffer_ptrw[i * 8 + 6] = 0;
  504. multimesh_buffer_ptrw[i * 8 + 7] = handle_transform[2][1];
  505. }
  506. rs->multimesh_set_buffer(debug_handle_smooth_multimesh_rid, multimesh_buffer);
  507. rs->multimesh_set_visible_instances(debug_handle_smooth_multimesh_rid, handle_smooth_count);
  508. rs->canvas_item_add_multimesh(vpc->get_canvas_item(), debug_handle_smooth_multimesh_rid, curve_handle->get_rid());
  509. }
  510. }
  511. void Path2DEditor::_node_visibility_changed() {
  512. if (!node) {
  513. return;
  514. }
  515. canvas_item_editor->update_viewport();
  516. _update_toolbar();
  517. }
  518. void Path2DEditor::_update_toolbar() {
  519. if (!node) {
  520. return;
  521. }
  522. bool has_curve = node->get_curve().is_valid();
  523. toolbar->set_visible(has_curve);
  524. create_curve_button->set_visible(!has_curve);
  525. }
  526. void Path2DEditor::edit(Node *p_path2d) {
  527. if (!canvas_item_editor) {
  528. canvas_item_editor = CanvasItemEditor::get_singleton();
  529. }
  530. if (action != ACTION_NONE) {
  531. _cancel_current_action();
  532. }
  533. if (p_path2d) {
  534. node = Object::cast_to<Path2D>(p_path2d);
  535. _update_toolbar();
  536. if (!node->is_connected(SceneStringName(visibility_changed), callable_mp(this, &Path2DEditor::_node_visibility_changed))) {
  537. node->connect(SceneStringName(visibility_changed), callable_mp(this, &Path2DEditor::_node_visibility_changed));
  538. }
  539. } else {
  540. // The node may have been deleted at this point.
  541. if (node && node->is_connected(SceneStringName(visibility_changed), callable_mp(this, &Path2DEditor::_node_visibility_changed))) {
  542. node->disconnect(SceneStringName(visibility_changed), callable_mp(this, &Path2DEditor::_node_visibility_changed));
  543. }
  544. node = nullptr;
  545. }
  546. canvas_item_editor->update_viewport();
  547. }
  548. void Path2DEditor::_bind_methods() {
  549. ClassDB::bind_method(D_METHOD("_update_toolbar"), &Path2DEditor::_update_toolbar);
  550. ClassDB::bind_method(D_METHOD("_clear_curve_points"), &Path2DEditor::_clear_curve_points);
  551. ClassDB::bind_method(D_METHOD("_restore_curve_points"), &Path2DEditor::_restore_curve_points);
  552. }
  553. void Path2DEditor::_mode_selected(int p_mode) {
  554. if (p_mode == MODE_CREATE) {
  555. curve_create->set_pressed(true);
  556. curve_edit->set_pressed(false);
  557. curve_edit_curve->set_pressed(false);
  558. curve_del->set_pressed(false);
  559. } else if (p_mode == MODE_EDIT) {
  560. curve_create->set_pressed(false);
  561. curve_edit->set_pressed(true);
  562. curve_edit_curve->set_pressed(false);
  563. curve_del->set_pressed(false);
  564. } else if (p_mode == MODE_EDIT_CURVE) {
  565. curve_create->set_pressed(false);
  566. curve_edit->set_pressed(false);
  567. curve_edit_curve->set_pressed(true);
  568. curve_del->set_pressed(false);
  569. } else if (p_mode == MODE_DELETE) {
  570. curve_create->set_pressed(false);
  571. curve_edit->set_pressed(false);
  572. curve_edit_curve->set_pressed(false);
  573. curve_del->set_pressed(true);
  574. } else if (p_mode == MODE_CLOSE) {
  575. if (node->get_curve().is_null()) {
  576. return;
  577. }
  578. if (node->get_curve()->get_point_count() < 3) {
  579. return;
  580. }
  581. Vector2 begin = node->get_curve()->get_point_position(0);
  582. Vector2 end = node->get_curve()->get_point_position(node->get_curve()->get_point_count() - 1);
  583. if (begin.is_equal_approx(end)) {
  584. return;
  585. }
  586. EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
  587. undo_redo->create_action(TTR("Close the Curve"));
  588. undo_redo->add_do_method(node->get_curve().ptr(), "add_point", begin);
  589. undo_redo->add_undo_method(node->get_curve().ptr(), "remove_point", node->get_curve()->get_point_count());
  590. undo_redo->add_do_method(canvas_item_editor, "update_viewport");
  591. undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
  592. undo_redo->commit_action();
  593. return;
  594. } else if (p_mode == MODE_CLEAR_POINTS) {
  595. if (node->get_curve().is_null()) {
  596. return;
  597. }
  598. if (node->get_curve()->get_point_count() == 0) {
  599. return;
  600. }
  601. EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
  602. PackedVector2Array points = node->get_curve()->get_points().duplicate();
  603. undo_redo->create_action(TTR("Clear Curve Points"), UndoRedo::MERGE_DISABLE, node);
  604. undo_redo->add_do_method(this, "_clear_curve_points", node);
  605. undo_redo->add_undo_method(this, "_restore_curve_points", node, points);
  606. undo_redo->add_do_method(canvas_item_editor, "update_viewport");
  607. undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
  608. undo_redo->commit_action();
  609. return;
  610. }
  611. mode = Mode(p_mode);
  612. }
  613. void Path2DEditor::_handle_option_pressed(int p_option) {
  614. PopupMenu *pm;
  615. pm = handle_menu->get_popup();
  616. switch (p_option) {
  617. case HANDLE_OPTION_ANGLE: {
  618. bool is_checked = pm->is_item_checked(HANDLE_OPTION_ANGLE);
  619. mirror_handle_angle = !is_checked;
  620. pm->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle);
  621. pm->set_item_disabled(HANDLE_OPTION_LENGTH, !mirror_handle_angle);
  622. } break;
  623. case HANDLE_OPTION_LENGTH: {
  624. bool is_checked = pm->is_item_checked(HANDLE_OPTION_LENGTH);
  625. mirror_handle_length = !is_checked;
  626. pm->set_item_checked(HANDLE_OPTION_LENGTH, mirror_handle_length);
  627. } break;
  628. }
  629. }
  630. void Path2DEditor::_cancel_current_action() {
  631. ERR_FAIL_NULL(node);
  632. Ref<Curve2D> curve = node->get_curve();
  633. ERR_FAIL_COND(curve.is_null());
  634. switch (action) {
  635. case ACTION_MOVING_POINT: {
  636. curve->set_point_position(action_point, moving_from);
  637. } break;
  638. case ACTION_MOVING_NEW_POINT: {
  639. curve->remove_point(curve->get_point_count() - 1);
  640. } break;
  641. case ACTION_MOVING_NEW_POINT_FROM_SPLIT: {
  642. curve->remove_point(action_point);
  643. } break;
  644. case ACTION_MOVING_IN: {
  645. curve->set_point_in(action_point, moving_from);
  646. curve->set_point_out(action_point, mirror_handle_length ? -moving_from : (-moving_from.normalized() * orig_out_length));
  647. } break;
  648. case ACTION_MOVING_OUT: {
  649. curve->set_point_out(action_point, moving_from);
  650. curve->set_point_in(action_point, mirror_handle_length ? -moving_from : (-moving_from.normalized() * orig_in_length));
  651. } break;
  652. default: {
  653. }
  654. }
  655. canvas_item_editor->update_viewport();
  656. action = ACTION_NONE;
  657. }
  658. void Path2DEditor::_create_curve() {
  659. ERR_FAIL_NULL(node);
  660. Ref<Curve2D> new_curve;
  661. new_curve.instantiate();
  662. EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
  663. undo_redo->create_action(TTR("Create Curve in Path2D"));
  664. undo_redo->add_do_property(node, "curve", new_curve);
  665. undo_redo->add_undo_property(node, "curve", Ref<Curve2D>());
  666. undo_redo->add_do_method(this, "_update_toolbar");
  667. undo_redo->add_undo_method(this, "_update_toolbar");
  668. undo_redo->commit_action();
  669. }
  670. void Path2DEditor::_confirm_clear_points() {
  671. if (!node || node->get_curve().is_null()) {
  672. return;
  673. }
  674. if (node->get_curve()->get_point_count() == 0) {
  675. return;
  676. }
  677. clear_points_dialog->reset_size();
  678. clear_points_dialog->popup_centered();
  679. }
  680. void Path2DEditor::_clear_curve_points(Path2D *p_path2d) {
  681. if (!p_path2d || p_path2d->get_curve().is_null()) {
  682. return;
  683. }
  684. Ref<Curve2D> curve = p_path2d->get_curve();
  685. if (curve->get_point_count() == 0) {
  686. return;
  687. }
  688. curve->clear_points();
  689. if (node == p_path2d) {
  690. _mode_selected(MODE_CREATE);
  691. }
  692. }
  693. void Path2DEditor::_restore_curve_points(Path2D *p_path2d, const PackedVector2Array &p_points) {
  694. if (!p_path2d || p_path2d->get_curve().is_null()) {
  695. return;
  696. }
  697. Ref<Curve2D> curve = p_path2d->get_curve();
  698. if (curve->get_point_count() > 0) {
  699. curve->clear_points();
  700. }
  701. for (int i = 0; i < p_points.size(); i += 3) {
  702. curve->add_point(p_points[i + 2], p_points[i], p_points[i + 1]); // The Curve2D::points pattern is [point_in, point_out, point_position].
  703. }
  704. if (node == p_path2d) {
  705. _mode_selected(MODE_EDIT);
  706. }
  707. }
  708. Path2DEditor::Path2DEditor() {
  709. toolbar = memnew(HBoxContainer);
  710. curve_edit = memnew(Button);
  711. curve_edit->set_theme_type_variation(SceneStringName(FlatButton));
  712. curve_edit->set_toggle_mode(true);
  713. curve_edit->set_pressed(true);
  714. curve_edit->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
  715. curve_edit->set_tooltip_text(TTR("Select Points") + "\n" + TTR("Shift+Drag: Select Control Points") + "\n" + vformat(TTR("%s+Click: Add Point"), keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL)) + "\n" + TTR("Left Click: Split Segment (in curve)") + "\n" + TTR("Right Click: Delete Point"));
  716. curve_edit->set_accessibility_name(TTRC("Select Points"));
  717. curve_edit->connect(SceneStringName(pressed), callable_mp(this, &Path2DEditor::_mode_selected).bind(MODE_EDIT));
  718. toolbar->add_child(curve_edit);
  719. curve_edit_curve = memnew(Button);
  720. curve_edit_curve->set_theme_type_variation(SceneStringName(FlatButton));
  721. curve_edit_curve->set_toggle_mode(true);
  722. curve_edit_curve->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
  723. curve_edit_curve->set_tooltip_text(TTR("Select Control Points (Shift+Drag)"));
  724. curve_edit_curve->set_accessibility_name(TTRC("Select Control Points"));
  725. curve_edit_curve->connect(SceneStringName(pressed), callable_mp(this, &Path2DEditor::_mode_selected).bind(MODE_EDIT_CURVE));
  726. toolbar->add_child(curve_edit_curve);
  727. curve_create = memnew(Button);
  728. curve_create->set_theme_type_variation(SceneStringName(FlatButton));
  729. curve_create->set_toggle_mode(true);
  730. curve_create->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
  731. curve_create->set_tooltip_text(TTR("Add Point (in empty space)") + "\n" + TTR("Right Click: Delete Point"));
  732. curve_create->set_accessibility_name(TTRC("Add Point (in empty space)"));
  733. curve_create->connect(SceneStringName(pressed), callable_mp(this, &Path2DEditor::_mode_selected).bind(MODE_CREATE));
  734. toolbar->add_child(curve_create);
  735. curve_del = memnew(Button);
  736. curve_del->set_theme_type_variation(SceneStringName(FlatButton));
  737. curve_del->set_toggle_mode(true);
  738. curve_del->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
  739. curve_del->set_tooltip_text(TTR("Delete Point"));
  740. curve_del->connect(SceneStringName(pressed), callable_mp(this, &Path2DEditor::_mode_selected).bind(MODE_DELETE));
  741. toolbar->add_child(curve_del);
  742. curve_close = memnew(Button);
  743. curve_close->set_theme_type_variation(SceneStringName(FlatButton));
  744. curve_close->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
  745. curve_close->set_tooltip_text(TTR("Close Curve"));
  746. curve_close->connect(SceneStringName(pressed), callable_mp(this, &Path2DEditor::_mode_selected).bind(MODE_CLOSE));
  747. toolbar->add_child(curve_close);
  748. curve_clear_points = memnew(Button);
  749. curve_clear_points->set_theme_type_variation(SceneStringName(FlatButton));
  750. curve_clear_points->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
  751. curve_clear_points->set_tooltip_text(TTR("Clear Points"));
  752. curve_clear_points->connect(SceneStringName(pressed), callable_mp(this, &Path2DEditor::_confirm_clear_points));
  753. toolbar->add_child(curve_clear_points);
  754. clear_points_dialog = memnew(ConfirmationDialog);
  755. clear_points_dialog->set_title(TTR("Please Confirm..."));
  756. clear_points_dialog->set_text(TTR("Remove all curve points?"));
  757. clear_points_dialog->connect(SceneStringName(confirmed), callable_mp(this, &Path2DEditor::_mode_selected).bind(MODE_CLEAR_POINTS));
  758. toolbar->add_child(clear_points_dialog);
  759. handle_menu = memnew(MenuButton);
  760. handle_menu->set_flat(false);
  761. handle_menu->set_theme_type_variation("FlatMenuButton");
  762. handle_menu->set_text(TTR("Options"));
  763. toolbar->add_child(handle_menu);
  764. PopupMenu *menu = handle_menu->get_popup();
  765. menu->add_check_item(TTR("Mirror Handle Angles"));
  766. menu->set_item_checked(HANDLE_OPTION_ANGLE, mirror_handle_angle);
  767. menu->add_check_item(TTR("Mirror Handle Lengths"));
  768. menu->set_item_checked(HANDLE_OPTION_LENGTH, mirror_handle_length);
  769. menu->connect(SceneStringName(id_pressed), callable_mp(this, &Path2DEditor::_handle_option_pressed));
  770. add_child(toolbar);
  771. create_curve_button = memnew(Button);
  772. create_curve_button->set_text(TTR("Create Curve"));
  773. create_curve_button->hide();
  774. add_child(create_curve_button);
  775. create_curve_button->connect(SceneStringName(pressed), callable_mp(this, &Path2DEditor::_create_curve));
  776. ERR_FAIL_NULL(RS::get_singleton());
  777. RenderingServer *rs = RS::get_singleton();
  778. debug_mesh_rid = rs->mesh_create();
  779. {
  780. debug_handle_mesh_rid = rs->mesh_create();
  781. Vector<Vector2> vertex_array;
  782. vertex_array.resize(4);
  783. Vector2 *vertex_array_ptrw = vertex_array.ptrw();
  784. vertex_array_ptrw[0] = Vector2(-1.0, -1.0);
  785. vertex_array_ptrw[1] = Vector2(1.0, -1.0);
  786. vertex_array_ptrw[2] = Vector2(1.0, 1.0);
  787. vertex_array_ptrw[3] = Vector2(-1.0, 1.0);
  788. Vector<Vector2> uv_array;
  789. uv_array.resize(4);
  790. Vector2 *uv_array_ptrw = uv_array.ptrw();
  791. uv_array_ptrw[0] = Vector2(0.0, 0.0);
  792. uv_array_ptrw[1] = Vector2(1.0, 0.0);
  793. uv_array_ptrw[2] = Vector2(1.0, 1.0);
  794. uv_array_ptrw[3] = Vector2(0.0, 1.0);
  795. Vector<int> index_array;
  796. index_array.resize(6);
  797. int *index_array_ptrw = index_array.ptrw();
  798. index_array_ptrw[0] = 0;
  799. index_array_ptrw[1] = 1;
  800. index_array_ptrw[2] = 3;
  801. index_array_ptrw[3] = 1;
  802. index_array_ptrw[4] = 2;
  803. index_array_ptrw[5] = 3;
  804. Array mesh_arrays;
  805. mesh_arrays.resize(RS::ARRAY_MAX);
  806. mesh_arrays[RS::ARRAY_VERTEX] = vertex_array;
  807. mesh_arrays[RS::ARRAY_TEX_UV] = uv_array;
  808. mesh_arrays[RS::ARRAY_INDEX] = index_array;
  809. rs->mesh_add_surface_from_arrays(debug_handle_mesh_rid, RS::PRIMITIVE_TRIANGLES, mesh_arrays, Array(), Dictionary(), RS::ARRAY_FLAG_USE_2D_VERTICES);
  810. debug_handle_curve_multimesh_rid = rs->multimesh_create();
  811. debug_handle_sharp_multimesh_rid = rs->multimesh_create();
  812. debug_handle_smooth_multimesh_rid = rs->multimesh_create();
  813. rs->multimesh_set_mesh(debug_handle_curve_multimesh_rid, debug_handle_mesh_rid);
  814. rs->multimesh_set_mesh(debug_handle_sharp_multimesh_rid, debug_handle_mesh_rid);
  815. rs->multimesh_set_mesh(debug_handle_smooth_multimesh_rid, debug_handle_mesh_rid);
  816. }
  817. }
  818. Path2DEditor::~Path2DEditor() {
  819. ERR_FAIL_NULL(RS::get_singleton());
  820. RS::get_singleton()->free_rid(debug_mesh_rid);
  821. RS::get_singleton()->free_rid(debug_handle_curve_multimesh_rid);
  822. RS::get_singleton()->free_rid(debug_handle_sharp_multimesh_rid);
  823. RS::get_singleton()->free_rid(debug_handle_smooth_multimesh_rid);
  824. RS::get_singleton()->free_rid(debug_handle_mesh_rid);
  825. }
  826. void Path2DEditorPlugin::edit(Object *p_object) {
  827. path2d_editor->edit(Object::cast_to<Node>(p_object));
  828. }
  829. bool Path2DEditorPlugin::handles(Object *p_object) const {
  830. return p_object->is_class("Path2D");
  831. }
  832. void Path2DEditorPlugin::make_visible(bool p_visible) {
  833. if (p_visible) {
  834. path2d_editor->show();
  835. } else {
  836. path2d_editor->hide();
  837. path2d_editor->edit(nullptr);
  838. }
  839. }
  840. Path2DEditorPlugin::Path2DEditorPlugin() {
  841. path2d_editor = memnew(Path2DEditor);
  842. CanvasItemEditor::get_singleton()->add_control_to_menu_panel(path2d_editor);
  843. path2d_editor->hide();
  844. }