Path.h 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/ByteString.h>
  8. #include <AK/Optional.h>
  9. #include <AK/Vector.h>
  10. #include <LibGfx/Forward.h>
  11. #include <LibGfx/Line.h>
  12. #include <LibGfx/Point.h>
  13. #include <LibGfx/Rect.h>
  14. namespace Gfx {
  15. class Path;
  16. class PathSegment {
  17. public:
  18. enum Command : u8 {
  19. MoveTo,
  20. LineTo,
  21. QuadraticBezierCurveTo,
  22. CubicBezierCurveTo,
  23. };
  24. ALWAYS_INLINE Command command() const { return m_command; }
  25. ALWAYS_INLINE FloatPoint point() const { return m_points.last(); }
  26. ALWAYS_INLINE FloatPoint through() const
  27. {
  28. VERIFY(m_command == Command::QuadraticBezierCurveTo);
  29. return m_points[0];
  30. }
  31. ALWAYS_INLINE FloatPoint through_0() const
  32. {
  33. VERIFY(m_command == Command::CubicBezierCurveTo);
  34. return m_points[0];
  35. }
  36. ALWAYS_INLINE FloatPoint through_1() const
  37. {
  38. VERIFY(m_command == Command::CubicBezierCurveTo);
  39. return m_points[1];
  40. }
  41. ALWAYS_INLINE ReadonlySpan<FloatPoint> points() const { return m_points; }
  42. static constexpr int points_per_command(Command command)
  43. {
  44. switch (command) {
  45. case Command::MoveTo:
  46. case Command::LineTo:
  47. return 1; // Single point.
  48. case Command::QuadraticBezierCurveTo:
  49. return 2; // Control point + point.
  50. case Command::CubicBezierCurveTo:
  51. return 3; // Two control points + point.
  52. }
  53. VERIFY_NOT_REACHED();
  54. }
  55. PathSegment(Command command, ReadonlySpan<FloatPoint> points)
  56. : m_command(command)
  57. , m_points(points) {};
  58. private:
  59. Command m_command;
  60. ReadonlySpan<FloatPoint> m_points;
  61. };
  62. class PathSegmentIterator {
  63. public:
  64. int operator<=>(PathSegmentIterator other) const
  65. {
  66. if (m_command_index > other.m_command_index)
  67. return 1;
  68. if (m_command_index < other.m_command_index)
  69. return -1;
  70. return 0;
  71. }
  72. bool operator==(PathSegmentIterator other) const { return m_command_index == other.m_command_index; }
  73. bool operator!=(PathSegmentIterator other) const { return m_command_index != other.m_command_index; }
  74. PathSegmentIterator operator++()
  75. {
  76. if (m_command_index < m_commands.size())
  77. m_point_index += PathSegment::points_per_command(m_commands[m_command_index++]);
  78. return *this;
  79. }
  80. PathSegmentIterator operator++(int)
  81. {
  82. PathSegmentIterator old(*this);
  83. ++*this;
  84. return old;
  85. }
  86. PathSegmentIterator operator--()
  87. {
  88. if (m_command_index > 0)
  89. m_point_index -= PathSegment::points_per_command(m_commands[--m_command_index]);
  90. return *this;
  91. }
  92. PathSegmentIterator operator--(int)
  93. {
  94. PathSegmentIterator old(*this);
  95. --*this;
  96. return old;
  97. }
  98. PathSegment operator*() const
  99. {
  100. auto command = m_commands[m_command_index];
  101. return PathSegment { command, m_points.span().slice(m_point_index, PathSegment::points_per_command(command)) };
  102. }
  103. PathSegmentIterator& operator=(PathSegmentIterator const& other)
  104. {
  105. m_point_index = other.m_point_index;
  106. m_command_index = other.m_command_index;
  107. return *this;
  108. }
  109. PathSegmentIterator(PathSegmentIterator const&) = default;
  110. friend Path;
  111. private:
  112. PathSegmentIterator(Vector<FloatPoint> const& points, Vector<PathSegment::Command> const& commands, size_t point_index = 0, size_t command_index = 0)
  113. : m_points(points)
  114. , m_commands(commands)
  115. , m_point_index(point_index)
  116. , m_command_index(command_index)
  117. {
  118. }
  119. // Note: Store reference to vectors from Gfx::Path so appending segments does not invalidate iterators.
  120. Vector<FloatPoint> const& m_points;
  121. Vector<PathSegment::Command> const& m_commands;
  122. size_t m_point_index { 0 };
  123. size_t m_command_index { 0 };
  124. };
  125. class Path {
  126. public:
  127. Path() = default;
  128. void move_to(FloatPoint point)
  129. {
  130. append_segment<PathSegment::MoveTo>(point);
  131. }
  132. void line_to(FloatPoint point)
  133. {
  134. append_segment<PathSegment::LineTo>(point);
  135. invalidate_split_lines();
  136. }
  137. void horizontal_line_to(float x)
  138. {
  139. line_to({ x, last_point().y() });
  140. }
  141. void vertical_line_to(float y)
  142. {
  143. line_to({ last_point().x(), y });
  144. }
  145. void quadratic_bezier_curve_to(FloatPoint through, FloatPoint point)
  146. {
  147. append_segment<PathSegment::QuadraticBezierCurveTo>(through, point);
  148. invalidate_split_lines();
  149. }
  150. void cubic_bezier_curve_to(FloatPoint c1, FloatPoint c2, FloatPoint p2)
  151. {
  152. append_segment<PathSegment::CubicBezierCurveTo>(c1, c2, p2);
  153. invalidate_split_lines();
  154. }
  155. void elliptical_arc_to(FloatPoint point, FloatSize radii, float x_axis_rotation, bool large_arc, bool sweep);
  156. void arc_to(FloatPoint point, float radius, bool large_arc, bool sweep)
  157. {
  158. elliptical_arc_to(point, { radius, radius }, 0, large_arc, sweep);
  159. }
  160. void text(Utf8View, Font const&);
  161. FloatPoint last_point()
  162. {
  163. if (!m_points.is_empty())
  164. return m_points.last();
  165. return {};
  166. }
  167. void close();
  168. void close_all_subpaths();
  169. Path stroke_to_fill(float thickness) const;
  170. Path place_text_along(Utf8View text, Font const&) const;
  171. Path copy_transformed(AffineTransform const&) const;
  172. ReadonlySpan<FloatLine> split_lines() const
  173. {
  174. if (!m_split_lines.has_value()) {
  175. const_cast<Path*>(this)->segmentize_path();
  176. VERIFY(m_split_lines.has_value());
  177. }
  178. return m_split_lines->lines;
  179. }
  180. Gfx::FloatRect const& bounding_box() const
  181. {
  182. (void)split_lines();
  183. return m_split_lines->bounding_box;
  184. }
  185. void append_path(Path const& path)
  186. {
  187. m_commands.extend(path.m_commands);
  188. m_points.extend(path.m_points);
  189. invalidate_split_lines();
  190. }
  191. ByteString to_byte_string() const;
  192. PathSegmentIterator begin() const
  193. {
  194. return PathSegmentIterator(m_points, m_commands);
  195. }
  196. PathSegmentIterator end() const
  197. {
  198. return PathSegmentIterator(m_points, m_commands, m_points.size(), m_commands.size());
  199. }
  200. bool is_empty() const
  201. {
  202. return m_commands.is_empty();
  203. }
  204. void clear()
  205. {
  206. *this = Path {};
  207. }
  208. private:
  209. void approximate_elliptical_arc_with_cubic_beziers(FloatPoint center, FloatSize radii, float x_axis_rotation, float theta, float theta_delta);
  210. void invalidate_split_lines()
  211. {
  212. m_split_lines.clear();
  213. }
  214. void segmentize_path();
  215. template<PathSegment::Command command, typename... Args>
  216. void append_segment(Args&&... args)
  217. {
  218. constexpr auto point_count = sizeof...(Args);
  219. static_assert(point_count == PathSegment::points_per_command(command));
  220. FloatPoint points[] { args... };
  221. // Note: This should maintain the invariant that `m_points.last()` is always the last point in the path.
  222. m_points.append(points, point_count);
  223. m_commands.append(command);
  224. }
  225. Vector<FloatPoint> m_points {};
  226. Vector<PathSegment::Command> m_commands {};
  227. struct SplitLines {
  228. Vector<FloatLine> lines;
  229. Gfx::FloatRect bounding_box;
  230. };
  231. Optional<SplitLines> m_split_lines {};
  232. };
  233. }