GradientTool.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. /*
  2. * Copyright (c) 2023, Torsten Engelmann <engelTorsten@gmx.de>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "GradientTool.h"
  7. #include "../ImageEditor.h"
  8. #include "../Layer.h"
  9. #include <LibGUI/Action.h>
  10. #include <LibGUI/Application.h>
  11. #include <LibGUI/BoxLayout.h>
  12. #include <LibGUI/Button.h>
  13. #include <LibGUI/CheckBox.h>
  14. #include <LibGUI/ComboBox.h>
  15. #include <LibGUI/ItemListModel.h>
  16. #include <LibGUI/Label.h>
  17. #include <LibGUI/OpacitySlider.h>
  18. #include <LibGUI/Painter.h>
  19. #include <LibGfx/AntiAliasingPainter.h>
  20. #include <LibGfx/Color.h>
  21. #include <LibGfx/Gradients.h>
  22. #include <LibGfx/Path.h>
  23. #include <LibGfx/Rect.h>
  24. namespace PixelPaint {
  25. Variant<Gfx::StandardCursor, NonnullRefPtr<Gfx::Bitmap const>> GradientTool::cursor()
  26. {
  27. if (m_hover_over_drag_handle || m_hover_over_start_handle || m_hover_over_end_handle || m_hover_over_transversal_a_handle || m_hover_over_transversal_b_handle)
  28. return Gfx::StandardCursor::Hand;
  29. if (m_button_pressed)
  30. return Gfx::StandardCursor::Move;
  31. return Gfx::StandardCursor::Crosshair;
  32. }
  33. void GradientTool::on_mousedown(Layer* layer, MouseEvent& event)
  34. {
  35. if (!layer)
  36. return;
  37. auto& layer_event = event.layer_event();
  38. if (layer_event.button() != GUI::MouseButton::Primary && layer_event.button() != GUI::MouseButton::Secondary)
  39. return;
  40. m_button_pressed = true;
  41. if (!m_hover_over_start_handle && !m_hover_over_end_handle && !m_hover_over_transversal_a_handle && !m_hover_over_transversal_b_handle) {
  42. if (has_gradient_data()) {
  43. Gfx::IntPoint movement_delta = layer_event.position() - m_gradient_center.value();
  44. m_gradient_center = layer_event.position();
  45. move_gradient_position(movement_delta);
  46. calculate_gradient_lines();
  47. } else {
  48. m_gradient_center = layer_event.position();
  49. }
  50. }
  51. m_physical_diagonal_layer_length = Gfx::IntPoint(0, 0).distance_from({ layer->rect().width(), layer->rect().height() });
  52. m_editor->update_tool_cursor();
  53. }
  54. void GradientTool::on_mousemove(Layer* layer, MouseEvent& event)
  55. {
  56. // Check if user is hovering over a handle
  57. if (layer && m_editor && !m_button_pressed && has_gradient_data()) {
  58. auto set_hover_flag = [&](bool& flag, Optional<Gfx::IntPoint> const p) {
  59. auto handle_offset = m_editor->content_to_frame_position(layer->location());
  60. float scale = m_editor->scale();
  61. auto frame_postion = p.value().to_type<float>().scaled(scale, scale).translated(handle_offset).to_type<int>();
  62. auto handle = Gfx::IntRect::centered_at(frame_postion, { 16, 16 });
  63. if (flag != handle.contains(event.raw_event().position())) {
  64. flag = !flag;
  65. m_editor->update_tool_cursor();
  66. m_editor->update();
  67. }
  68. };
  69. set_hover_flag(m_hover_over_start_handle, m_gradient_start.value());
  70. set_hover_flag(m_hover_over_drag_handle, m_gradient_center.value());
  71. set_hover_flag(m_hover_over_end_handle, m_gradient_end.value());
  72. if (m_mode == GradientMode::Radial) {
  73. set_hover_flag(m_hover_over_transversal_a_handle, m_gradient_transversal_a.value());
  74. set_hover_flag(m_hover_over_transversal_b_handle, m_gradient_transversal_b.value());
  75. }
  76. }
  77. if (!layer || !m_button_pressed)
  78. return;
  79. auto& layer_event = event.layer_event();
  80. if (!m_hover_over_drag_handle && (m_hover_over_start_handle || m_hover_over_end_handle)) {
  81. auto movement_delta = m_hover_over_start_handle ? layer_event.position() - m_gradient_start.value() : layer_event.position() - m_gradient_end.value();
  82. rotate_gradient_points(m_hover_over_start_handle ? movement_delta.scaled({ -1, -1 }) : movement_delta);
  83. }
  84. if (!m_hover_over_drag_handle && (m_hover_over_transversal_a_handle || m_hover_over_transversal_b_handle)) {
  85. auto distance_to_center = layer_event.position().distance_from(m_gradient_center.value());
  86. auto new_left_right_distance_fraction = distance_to_center / m_gradient_center.value().distance_from(m_gradient_start.value());
  87. calculate_transversal_points(new_left_right_distance_fraction);
  88. }
  89. if (m_hover_over_drag_handle) {
  90. auto movement_delta = layer_event.position() - m_gradient_center.value();
  91. m_gradient_center.value().translate_by(movement_delta);
  92. move_gradient_position(movement_delta);
  93. }
  94. if (!(m_hover_over_drag_handle || m_hover_over_start_handle || m_hover_over_end_handle || m_hover_over_transversal_a_handle || m_hover_over_transversal_b_handle))
  95. update_gradient_with_initial_values(layer_event.position());
  96. // If Shift is pressed, align the gradient horizontally or vertically
  97. if (m_shift_pressed && has_gradient_data() && m_mode == GradientMode::Linear) {
  98. auto delta = m_gradient_center.value() - m_gradient_end.value();
  99. if (AK::abs(delta.x()) < AK::abs(delta.y())) {
  100. m_gradient_start.value().set_x(m_gradient_center.value().x());
  101. m_gradient_end.value().set_x(m_gradient_center.value().x());
  102. } else {
  103. m_gradient_start.value().set_y(m_gradient_center.value().y());
  104. m_gradient_end.value().set_y(m_gradient_center.value().y());
  105. }
  106. }
  107. calculate_gradient_lines();
  108. }
  109. void GradientTool::on_mouseup(Layer*, MouseEvent& event)
  110. {
  111. auto& layer_event = event.layer_event();
  112. if (layer_event.button() != GUI::MouseButton::Primary && layer_event.button() != GUI::MouseButton::Secondary)
  113. return;
  114. m_button_pressed = false;
  115. m_editor->update_tool_cursor();
  116. }
  117. bool GradientTool::on_keydown(GUI::KeyEvent& event)
  118. {
  119. if (event.key() == Key_LeftShift || event.key() == Key_RightShift) {
  120. m_shift_pressed = true;
  121. if (m_button_pressed)
  122. m_editor->update();
  123. return true;
  124. }
  125. if (event.key() == Key_Return) {
  126. rasterize_gradient();
  127. return true;
  128. }
  129. if (event.key() == Key_Escape) {
  130. reset();
  131. return true;
  132. }
  133. return Tool::on_keydown(event);
  134. }
  135. void GradientTool::on_keyup(GUI::KeyEvent& event)
  136. {
  137. Tool::on_keydown(event);
  138. if (event.key() == Key_Shift) {
  139. m_shift_pressed = false;
  140. event.accept();
  141. }
  142. }
  143. void GradientTool::on_second_paint(Layer const* layer, GUI::PaintEvent& event)
  144. {
  145. if (!layer || !has_gradient_data())
  146. return;
  147. GUI::Painter painter(*m_editor);
  148. painter.add_clip_rect(event.rect());
  149. auto gradient_clip_rect = m_editor->content_to_frame_rect(layer->relative_rect()).to_type<int>().intersected(m_editor->content_rect());
  150. draw_gradient(painter, true, m_editor->content_to_frame_position(layer->location()), m_editor->scale(), gradient_clip_rect);
  151. }
  152. void GradientTool::on_primary_color_change(Color)
  153. {
  154. if (has_gradient_data())
  155. m_editor->update();
  156. }
  157. void GradientTool::on_secondary_color_change(Color)
  158. {
  159. if (has_gradient_data())
  160. m_editor->update();
  161. }
  162. void GradientTool::on_tool_activation()
  163. {
  164. reset();
  165. }
  166. ErrorOr<GUI::Widget*> GradientTool::get_properties_widget()
  167. {
  168. if (!m_properties_widget) {
  169. auto properties_widget = TRY(GUI::Widget::try_create());
  170. (void)TRY(properties_widget->try_set_layout<GUI::VerticalBoxLayout>());
  171. auto mode_container = TRY(properties_widget->try_add<GUI::Widget>());
  172. mode_container->set_fixed_height(20);
  173. (void)TRY(mode_container->try_set_layout<GUI::HorizontalBoxLayout>());
  174. auto mode_label = TRY(mode_container->try_add<GUI::Label>(TRY("Gradient Type:"_string)));
  175. mode_label->set_text_alignment(Gfx::TextAlignment::CenterLeft);
  176. mode_label->set_fixed_size(80, 20);
  177. static constexpr auto s_mode_names = [] {
  178. Array<StringView, (int)GradientMode::__Count> names;
  179. for (size_t i = 0; i < names.size(); i++) {
  180. switch ((GradientMode)i) {
  181. case GradientMode::Linear:
  182. names[i] = "Linear"sv;
  183. break;
  184. case GradientMode::Radial:
  185. names[i] = "Radial"sv;
  186. break;
  187. default:
  188. break;
  189. }
  190. }
  191. return names;
  192. }();
  193. auto mode_combobox = TRY(mode_container->try_add<GUI::ComboBox>());
  194. mode_combobox->set_only_allow_values_from_model(true);
  195. mode_combobox->set_model(*GUI::ItemListModel<StringView, decltype(s_mode_names)>::create(s_mode_names));
  196. mode_combobox->set_selected_index((int)m_mode, GUI::AllowCallback::No);
  197. auto opacity_container = TRY(properties_widget->try_add<GUI::Widget>());
  198. opacity_container->set_fixed_height(20);
  199. (void)TRY(opacity_container->try_set_layout<GUI::HorizontalBoxLayout>());
  200. auto opacity_label = TRY(opacity_container->try_add<GUI::Label>(TRY("Opacity:"_string)));
  201. opacity_label->set_text_alignment(Gfx::TextAlignment::CenterLeft);
  202. opacity_label->set_fixed_size(80, 20);
  203. auto opacity_slider = TRY(opacity_container->try_add<GUI::HorizontalOpacitySlider>());
  204. opacity_slider->set_range(1, 100);
  205. opacity_slider->set_value(100);
  206. opacity_slider->on_change = [this](int value) {
  207. m_opacity = value;
  208. m_editor->update();
  209. };
  210. set_primary_slider(opacity_slider);
  211. auto hardness_container = TRY(properties_widget->try_add<GUI::Widget>());
  212. (void)TRY(hardness_container->try_set_layout<GUI::HorizontalBoxLayout>());
  213. hardness_container->set_fixed_height(20);
  214. hardness_container->set_visible(m_mode == GradientMode::Radial);
  215. mode_combobox->on_change = [this, hardness_container](auto&, auto& model_index) {
  216. VERIFY(model_index.row() >= 0);
  217. VERIFY(model_index.row() < (int)GradientMode::__Count);
  218. GradientMode selected_mode = model_index.row() == 0 ? GradientMode::Linear : GradientMode::Radial;
  219. if (m_mode != selected_mode) {
  220. m_mode = selected_mode;
  221. reset();
  222. }
  223. hardness_container->set_visible(m_mode == GradientMode::Radial);
  224. };
  225. auto hardness_label = TRY(hardness_container->try_add<GUI::Label>(TRY("Hardness:"_string)));
  226. hardness_label->set_text_alignment(Gfx::TextAlignment::CenterLeft);
  227. hardness_label->set_fixed_size(80, 20);
  228. auto hardness_slider = TRY(hardness_container->try_add<GUI::HorizontalOpacitySlider>());
  229. hardness_slider->set_range(1, 99);
  230. hardness_slider->set_value(m_hardness);
  231. hardness_slider->on_change = [this](int value) {
  232. if (m_mode == GradientMode::Radial && m_editor) {
  233. m_hardness = value;
  234. m_editor->update();
  235. }
  236. };
  237. set_secondary_slider(hardness_slider);
  238. auto use_secondary_color_checkbox = TRY(properties_widget->try_add<GUI::CheckBox>(TRY("Use secondary color"_string)));
  239. use_secondary_color_checkbox->on_checked = [this](bool checked) {
  240. m_use_secondary_color = checked;
  241. m_editor->update();
  242. };
  243. auto button_container = TRY(properties_widget->try_add<GUI::Widget>());
  244. button_container->set_fixed_height(22);
  245. TRY(button_container->try_set_layout<GUI::HorizontalBoxLayout>());
  246. button_container->add_spacer().release_value_but_fixme_should_propagate_errors();
  247. auto apply_button = TRY(button_container->try_add<GUI::DialogButton>("Apply"_short_string));
  248. apply_button->on_click = [this](auto) {
  249. rasterize_gradient();
  250. };
  251. m_properties_widget = properties_widget;
  252. }
  253. return m_properties_widget.ptr();
  254. }
  255. void GradientTool::rasterize_gradient()
  256. {
  257. auto layer = m_editor->active_layer();
  258. if (!layer || !has_gradient_data())
  259. return;
  260. GUI::Painter painter(layer->get_scratch_edited_bitmap());
  261. draw_gradient(painter);
  262. layer->did_modify_bitmap(layer->get_scratch_edited_bitmap().rect());
  263. m_editor->did_complete_action(tool_name());
  264. reset();
  265. }
  266. void GradientTool::calculate_gradient_lines()
  267. {
  268. m_gradient_half_length = m_gradient_end.value().distance_from(m_gradient_center.value());
  269. // Create a perpendicular point between the center and end point.
  270. m_perpendicular_point = m_gradient_end.value();
  271. m_perpendicular_point -= m_gradient_center.value();
  272. m_perpendicular_point = { -m_perpendicular_point.y(), m_perpendicular_point.x() };
  273. m_perpendicular_point += m_gradient_center.value();
  274. auto to_edge_scale_direction = (m_physical_diagonal_layer_length * 2) / m_gradient_center.value().distance_from(m_perpendicular_point);
  275. m_gradient_center_line.set_a({ m_gradient_center.value().x() + (to_edge_scale_direction * (m_gradient_center.value().x() - m_perpendicular_point.x())), m_gradient_center.value().y() + (to_edge_scale_direction * (m_gradient_center.value().y() - m_perpendicular_point.y())) });
  276. m_gradient_center_line.set_b({ m_gradient_center.value().x() + (-to_edge_scale_direction * (m_gradient_center.value().x() - m_perpendicular_point.x())), m_gradient_center.value().y() + (-to_edge_scale_direction * (m_gradient_center.value().y() - m_perpendicular_point.y())) });
  277. m_gradient_begin_line.set_a(m_gradient_center_line.a().translated(static_cast<Gfx::FloatPoint>(m_gradient_end.value() - m_gradient_center.value())));
  278. m_gradient_begin_line.set_b(m_gradient_center_line.b().translated(static_cast<Gfx::FloatPoint>(m_gradient_end.value() - m_gradient_center.value())));
  279. m_gradient_end_line.set_a(m_gradient_center_line.a().translated(static_cast<Gfx::FloatPoint>(m_gradient_center.value() - m_gradient_end.value())));
  280. m_gradient_end_line.set_b(m_gradient_center_line.b().translated(static_cast<Gfx::FloatPoint>(m_gradient_center.value() - m_gradient_end.value())));
  281. m_editor->update();
  282. }
  283. void GradientTool::draw_gradient(GUI::Painter& painter, bool with_guidelines, const Gfx::FloatPoint drawing_offset, float scale, Optional<Gfx::IntRect const&> gradient_clip)
  284. {
  285. auto t_gradient_begin_line = m_gradient_begin_line.scaled(scale, scale).translated(drawing_offset);
  286. auto t_gradient_center_line = m_gradient_center_line.scaled(scale, scale).translated(drawing_offset);
  287. auto t_gradient_end_line = m_gradient_end_line.scaled(scale, scale).translated(drawing_offset);
  288. auto t_gradient_center = m_gradient_center.value().to_type<float>().scaled(scale, scale).translated(drawing_offset).to_type<int>();
  289. int width = m_editor->active_layer()->rect().width() * scale;
  290. int height = m_editor->active_layer()->rect().height() * scale;
  291. float rotation_radians = atan2f(t_gradient_begin_line.a().y() - t_gradient_end_line.a().y(), t_gradient_begin_line.a().x() - t_gradient_end_line.a().x());
  292. float rotation_degrees = (rotation_radians * 180) / AK::Pi<float>;
  293. auto determine_required_side_length = [&](int center, int side_length) {
  294. if (center < 0)
  295. return 2 * (AK::abs(center) + side_length);
  296. if (center > side_length)
  297. return 2 * center;
  298. return 2 * (AK::max(center + side_length, side_length - center));
  299. };
  300. auto scaled_gradient_center = m_gradient_center.value().to_type<float>().scaled(scale, scale).to_type<int>();
  301. auto gradient_rect_height = determine_required_side_length(t_gradient_center.y(), height);
  302. auto gradient_rect_width = determine_required_side_length(t_gradient_center.x(), width);
  303. auto gradient_max_side_length = AK::max(gradient_rect_height, gradient_rect_width);
  304. auto gradient_rect = Gfx::IntRect::centered_at(t_gradient_center, { gradient_max_side_length, gradient_max_side_length });
  305. float overall_gradient_length_in_rect = Gfx::calculate_gradient_length(gradient_rect.size(), rotation_degrees - 90);
  306. if (m_gradient_half_length == 0 || overall_gradient_length_in_rect == 0 || isnan(overall_gradient_length_in_rect))
  307. return;
  308. auto gradient_half_width_percentage_offset = (m_gradient_half_length * scale) / overall_gradient_length_in_rect;
  309. auto start_color = m_editor->color_for(GUI::MouseButton::Primary);
  310. start_color.set_alpha(start_color.alpha() * m_opacity / 100);
  311. auto end_color = [&] {
  312. if (m_use_secondary_color) {
  313. auto color = m_editor->color_for(GUI::MouseButton::Secondary);
  314. return color.with_alpha(color.alpha() * m_opacity / 100);
  315. }
  316. return start_color.with_alpha(0);
  317. }();
  318. {
  319. Gfx::PainterStateSaver saver(painter);
  320. if (gradient_clip.has_value())
  321. painter.add_clip_rect(*gradient_clip);
  322. switch (m_mode) {
  323. case GradientMode::__Count:
  324. break;
  325. case GradientMode::Linear:
  326. painter.fill_rect_with_linear_gradient(gradient_rect, Array { Gfx::ColorStop { start_color, 0.5f - gradient_half_width_percentage_offset }, Gfx::ColorStop { end_color, 0.5f + gradient_half_width_percentage_offset } }, rotation_degrees - 90);
  327. break;
  328. case GradientMode::Radial:
  329. auto t_gradient_longitudinal = m_gradient_start.value().to_type<float>().scaled(scale, scale).translated(drawing_offset).to_type<int>();
  330. auto t_gradient_transversal = m_gradient_transversal_a.value().to_type<float>().scaled(scale, scale).translated(drawing_offset).to_type<int>();
  331. auto radial_size = Gfx::IntSize((AK::abs(t_gradient_center.distance_from(t_gradient_longitudinal))), (AK::abs(t_gradient_center.distance_from(t_gradient_transversal))));
  332. AK::Array<Gfx::ColorStop, 3> colors = {
  333. Gfx::ColorStop { .color = start_color, .position = 0.0f },
  334. Gfx::ColorStop { .color = start_color, .position = m_hardness / 100.0f },
  335. Gfx::ColorStop { .color = end_color, .position = 1.0f },
  336. };
  337. painter.fill_rect_with_radial_gradient(Gfx::IntRect(drawing_offset, { width, height }), colors, scaled_gradient_center, radial_size, {}, 180 - rotation_degrees);
  338. break;
  339. }
  340. }
  341. if (with_guidelines) {
  342. Gfx::AntiAliasingPainter aa_painter = Gfx::AntiAliasingPainter(painter);
  343. Gfx::FloatLine icon_line1_rotated_offset = Gfx::FloatLine({ -2, -4 }, { -2, 4 }).rotated(rotation_radians);
  344. Gfx::FloatLine icon_line2_rotated_offset = Gfx::FloatLine({ 2, -4 }, { 2, 4 }).rotated(rotation_radians);
  345. Gfx::FloatLine icon_line3_rotated_offset = Gfx::FloatLine({ -3, -2 }, { -3, 2 }).rotated(rotation_radians);
  346. Gfx::FloatLine icon_line4_rotated_offset = Gfx::FloatLine({ 3, -2 }, { 3, 2 }).rotated(rotation_radians);
  347. Gfx::FloatLine icon_line5_rotated_offset = Gfx::FloatLine({ 0, -5 }, { 0, 5 }).rotated(rotation_radians);
  348. auto draw_handle = [&](Gfx::IntPoint p, bool is_hovered, IconStyle with_icon) {
  349. auto alpha = is_hovered ? 255 : 100;
  350. auto translated_p = p.to_type<float>().scaled(scale, scale).translated(drawing_offset);
  351. aa_painter.fill_circle(translated_p.to_type<int>(), 10, Color(Color::MidGray).with_alpha(alpha));
  352. aa_painter.fill_circle(translated_p.to_type<int>(), 8, Color(Color::LightGray).with_alpha(alpha));
  353. if (with_icon == IconStyle::ChangeWidthAndAngle) {
  354. aa_painter.draw_line(icon_line1_rotated_offset.translated(translated_p), Color(Color::MidGray).with_alpha(alpha), 2);
  355. aa_painter.draw_line(icon_line2_rotated_offset.translated(translated_p), Color(Color::MidGray).with_alpha(alpha), 2);
  356. }
  357. if (with_icon == IconStyle::RadialWidth) {
  358. auto make_triangle_path = [&](Gfx::FloatPoint p1, Gfx::FloatPoint p2, Gfx::FloatPoint p3) {
  359. Gfx::Path triangle;
  360. triangle.move_to(p1.translated(translated_p));
  361. triangle.line_to(p2.translated(translated_p));
  362. triangle.line_to(p3.translated(translated_p));
  363. triangle.close();
  364. return triangle;
  365. };
  366. aa_painter.fill_path(make_triangle_path(
  367. icon_line3_rotated_offset.a(),
  368. icon_line4_rotated_offset.a(),
  369. icon_line5_rotated_offset.a()),
  370. Color(Color::MidGray).with_alpha(alpha), Gfx::Painter::WindingRule::EvenOdd);
  371. aa_painter.fill_path(make_triangle_path(
  372. icon_line3_rotated_offset.b(),
  373. icon_line4_rotated_offset.b(),
  374. icon_line5_rotated_offset.b()),
  375. Color(Color::MidGray).with_alpha(alpha), Gfx::Painter::WindingRule::EvenOdd);
  376. }
  377. };
  378. if (m_mode == GradientMode::Linear) {
  379. aa_painter.draw_line(t_gradient_begin_line, Color::Black);
  380. aa_painter.draw_line(t_gradient_center_line, Color::MidGray);
  381. aa_painter.draw_line(t_gradient_end_line, Color::LightGray);
  382. } else {
  383. draw_handle(m_gradient_transversal_a.value(), m_hover_over_transversal_a_handle, IconStyle::RadialWidth);
  384. draw_handle(m_gradient_transversal_b.value(), m_hover_over_transversal_b_handle, IconStyle::RadialWidth);
  385. }
  386. draw_handle(m_gradient_start.value(), m_hover_over_start_handle, IconStyle::ChangeWidthAndAngle);
  387. draw_handle(m_gradient_center.value(), m_hover_over_drag_handle, IconStyle::None);
  388. draw_handle(m_gradient_end.value(), m_hover_over_end_handle, IconStyle::ChangeWidthAndAngle);
  389. }
  390. }
  391. void GradientTool::reset()
  392. {
  393. m_gradient_start = {};
  394. m_gradient_center = {};
  395. m_gradient_end = {};
  396. m_gradient_transversal_a = {};
  397. m_gradient_transversal_b = {};
  398. m_gradient_half_length = 0;
  399. m_physical_diagonal_layer_length = 0;
  400. m_hover_over_drag_handle = false;
  401. m_hover_over_start_handle = false;
  402. m_hover_over_end_handle = false;
  403. m_hover_over_transversal_a_handle = false;
  404. m_hover_over_transversal_b_handle = false;
  405. if (m_editor) {
  406. m_editor->update();
  407. m_editor->update_tool_cursor();
  408. }
  409. }
  410. void GradientTool::update_gradient_with_initial_values(Gfx::IntPoint const new_end_point)
  411. {
  412. VERIFY(m_gradient_center.has_value());
  413. m_gradient_end = new_end_point;
  414. auto deltaCenter = m_gradient_end.value() - m_gradient_center.value();
  415. m_gradient_start = m_gradient_center.value() - deltaCenter;
  416. if (m_mode == GradientMode::Radial) {
  417. Gfx::IntPoint perpendicularDeltaCenter = { -deltaCenter.y(), deltaCenter.x() };
  418. m_gradient_transversal_a = m_gradient_center.value() + perpendicularDeltaCenter;
  419. m_gradient_transversal_b = m_gradient_center.value() - perpendicularDeltaCenter;
  420. }
  421. }
  422. void GradientTool::move_gradient_position(Gfx::IntPoint const movement_delta)
  423. {
  424. m_gradient_end.value().translate_by(movement_delta);
  425. m_gradient_start.value().translate_by(movement_delta);
  426. if (m_mode == GradientMode::Radial) {
  427. m_gradient_transversal_a.value().translate_by(movement_delta);
  428. m_gradient_transversal_b.value().translate_by(movement_delta);
  429. }
  430. }
  431. void GradientTool::rotate_gradient_points(Gfx::IntPoint const delta)
  432. {
  433. m_gradient_end.value().translate_by(delta);
  434. auto translation_distance_to_center = m_gradient_center.value().distance_from(m_gradient_end.value()) - m_gradient_center.value().distance_from(m_gradient_start.value());
  435. m_gradient_start.value().translate_by(delta.scaled(-1, -1));
  436. if (m_mode == GradientMode::Radial) {
  437. auto new_horizontal_distance_fraction = (translation_distance_to_center + m_gradient_center.value().distance_from(m_gradient_transversal_a.value())) / m_gradient_center.value().distance_from(m_gradient_start.value());
  438. calculate_transversal_points(new_horizontal_distance_fraction);
  439. }
  440. }
  441. void GradientTool::calculate_transversal_points(float scale_fraction)
  442. {
  443. m_gradient_transversal_a = Gfx::IntPoint(m_gradient_center.value().x() + (scale_fraction * (m_gradient_center.value().x() - m_perpendicular_point.x())),
  444. m_gradient_center.value().y() + (scale_fraction * (m_gradient_center.value().y() - m_perpendicular_point.y())));
  445. m_gradient_transversal_b = Gfx::IntPoint(m_gradient_center.value().x() + (-scale_fraction * (m_gradient_center.value().x() - m_perpendicular_point.x())),
  446. m_gradient_center.value().y() + (-scale_fraction * (m_gradient_center.value().y() - m_perpendicular_point.y())));
  447. }
  448. }