1 | // Scintilla source code edit control |
2 | /** @file Geometry.h |
3 | ** Classes and functions for geometric and colour calculations. |
4 | **/ |
5 | // Copyright 2020 by Neil Hodgson <neilh@scintilla.org> |
6 | // The License.txt file describes the conditions under which this software may be distributed. |
7 | |
8 | #ifndef GEOMETRY_H |
9 | #define GEOMETRY_H |
10 | |
11 | namespace Scintilla::Internal { |
12 | |
13 | typedef double XYPOSITION; |
14 | typedef double XYACCUMULATOR; |
15 | |
16 | /** |
17 | * A geometric point class. |
18 | * Point is similar to the Win32 POINT and GTK+ GdkPoint types. |
19 | */ |
20 | class Point { |
21 | public: |
22 | XYPOSITION x; |
23 | XYPOSITION y; |
24 | |
25 | constexpr explicit Point(XYPOSITION x_=0, XYPOSITION y_=0) noexcept : x(x_), y(y_) { |
26 | } |
27 | |
28 | static constexpr Point FromInts(int x_, int y_) noexcept { |
29 | return Point(static_cast<XYPOSITION>(x_), static_cast<XYPOSITION>(y_)); |
30 | } |
31 | |
32 | constexpr bool operator==(Point other) const noexcept { |
33 | return (x == other.x) && (y == other.y); |
34 | } |
35 | |
36 | constexpr bool operator!=(Point other) const noexcept { |
37 | return (x != other.x) || (y != other.y); |
38 | } |
39 | |
40 | constexpr Point operator+(Point other) const noexcept { |
41 | return Point(x + other.x, y + other.y); |
42 | } |
43 | |
44 | constexpr Point operator-(Point other) const noexcept { |
45 | return Point(x - other.x, y - other.y); |
46 | } |
47 | |
48 | // Other automatically defined methods (assignment, copy constructor, destructor) are fine |
49 | }; |
50 | |
51 | |
52 | /** |
53 | * A geometric interval class. |
54 | */ |
55 | class Interval { |
56 | public: |
57 | XYPOSITION left; |
58 | XYPOSITION right; |
59 | constexpr bool operator==(const Interval &other) const noexcept { |
60 | return (left == other.left) && (right == other.right); |
61 | } |
62 | constexpr XYPOSITION Width() const noexcept { return right - left; } |
63 | constexpr bool Empty() const noexcept { |
64 | return Width() <= 0; |
65 | } |
66 | constexpr bool Intersects(Interval other) const noexcept { |
67 | return (right > other.left) && (left < other.right); |
68 | } |
69 | }; |
70 | |
71 | /** |
72 | * A geometric rectangle class. |
73 | * PRectangle is similar to Win32 RECT. |
74 | * PRectangles contain their top and left sides, but not their right and bottom sides. |
75 | */ |
76 | class PRectangle { |
77 | public: |
78 | XYPOSITION left; |
79 | XYPOSITION top; |
80 | XYPOSITION right; |
81 | XYPOSITION bottom; |
82 | |
83 | constexpr explicit PRectangle(XYPOSITION left_=0, XYPOSITION top_=0, XYPOSITION right_=0, XYPOSITION bottom_ = 0) noexcept : |
84 | left(left_), top(top_), right(right_), bottom(bottom_) { |
85 | } |
86 | |
87 | static constexpr PRectangle FromInts(int left_, int top_, int right_, int bottom_) noexcept { |
88 | return PRectangle(static_cast<XYPOSITION>(left_), static_cast<XYPOSITION>(top_), |
89 | static_cast<XYPOSITION>(right_), static_cast<XYPOSITION>(bottom_)); |
90 | } |
91 | |
92 | // Other automatically defined methods (assignment, copy constructor, destructor) are fine |
93 | |
94 | constexpr bool operator==(const PRectangle &rc) const noexcept { |
95 | return (rc.left == left) && (rc.right == right) && |
96 | (rc.top == top) && (rc.bottom == bottom); |
97 | } |
98 | constexpr bool Contains(Point pt) const noexcept { |
99 | return (pt.x >= left) && (pt.x <= right) && |
100 | (pt.y >= top) && (pt.y <= bottom); |
101 | } |
102 | constexpr bool ContainsWholePixel(Point pt) const noexcept { |
103 | // Does the rectangle contain all of the pixel to left/below the point |
104 | return (pt.x >= left) && ((pt.x+1) <= right) && |
105 | (pt.y >= top) && ((pt.y+1) <= bottom); |
106 | } |
107 | constexpr bool Contains(PRectangle rc) const noexcept { |
108 | return (rc.left >= left) && (rc.right <= right) && |
109 | (rc.top >= top) && (rc.bottom <= bottom); |
110 | } |
111 | constexpr bool Intersects(PRectangle other) const noexcept { |
112 | return (right > other.left) && (left < other.right) && |
113 | (bottom > other.top) && (top < other.bottom); |
114 | } |
115 | void Move(XYPOSITION xDelta, XYPOSITION yDelta) noexcept { |
116 | left += xDelta; |
117 | top += yDelta; |
118 | right += xDelta; |
119 | bottom += yDelta; |
120 | } |
121 | |
122 | constexpr PRectangle Inset(XYPOSITION delta) const noexcept { |
123 | return PRectangle(left + delta, top + delta, right - delta, bottom - delta); |
124 | } |
125 | |
126 | constexpr PRectangle Inset(Point delta) const noexcept { |
127 | return PRectangle(left + delta.x, top + delta.y, right - delta.x, bottom - delta.y); |
128 | } |
129 | |
130 | constexpr Point Centre() const noexcept { |
131 | return Point((left + right) / 2, (top + bottom) / 2); |
132 | } |
133 | |
134 | constexpr XYPOSITION Width() const noexcept { return right - left; } |
135 | constexpr XYPOSITION Height() const noexcept { return bottom - top; } |
136 | constexpr bool Empty() const noexcept { |
137 | return (Height() <= 0) || (Width() <= 0); |
138 | } |
139 | }; |
140 | |
141 | enum class Edge { left, top, bottom, right }; |
142 | |
143 | PRectangle Clamp(PRectangle rc, Edge edge, XYPOSITION position) noexcept; |
144 | PRectangle Side(PRectangle rc, Edge edge, XYPOSITION size) noexcept; |
145 | |
146 | Interval Intersection(Interval a, Interval b) noexcept; |
147 | PRectangle Intersection(PRectangle rc, Interval horizontalBounds) noexcept; |
148 | Interval HorizontalBounds(PRectangle rc) noexcept; |
149 | |
150 | XYPOSITION PixelAlign(XYPOSITION xy, int pixelDivisions) noexcept; |
151 | XYPOSITION PixelAlignFloor(XYPOSITION xy, int pixelDivisions) noexcept; |
152 | |
153 | Point PixelAlign(const Point &pt, int pixelDivisions) noexcept; |
154 | |
155 | PRectangle PixelAlign(const PRectangle &rc, int pixelDivisions) noexcept; |
156 | PRectangle PixelAlignOutside(const PRectangle &rc, int pixelDivisions) noexcept; |
157 | |
158 | /** |
159 | * Holds an RGBA colour with 8 bits for each component. |
160 | */ |
161 | constexpr const float componentMaximum = 255.0f; |
162 | class ColourRGBA { |
163 | int co; |
164 | public: |
165 | constexpr explicit ColourRGBA(int co_ = 0) noexcept : co(co_) { |
166 | } |
167 | |
168 | constexpr ColourRGBA(unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha=0xff) noexcept : |
169 | ColourRGBA(red | (green << 8) | (blue << 16) | (alpha << 24)) { |
170 | } |
171 | |
172 | constexpr ColourRGBA(ColourRGBA cd, unsigned int alpha) noexcept : |
173 | ColourRGBA(cd.OpaqueRGB() | (alpha << 24)) { |
174 | } |
175 | |
176 | static constexpr ColourRGBA FromRGB(int co_) noexcept { |
177 | return ColourRGBA(co_ | (0xffu << 24)); |
178 | } |
179 | |
180 | static constexpr ColourRGBA FromIpRGB(intptr_t co_) noexcept { |
181 | return ColourRGBA(static_cast<int>(co_) | (0xffu << 24)); |
182 | } |
183 | |
184 | constexpr ColourRGBA WithoutAlpha() const noexcept { |
185 | return ColourRGBA(co & 0xffffff); |
186 | } |
187 | |
188 | constexpr ColourRGBA Opaque() const noexcept { |
189 | return ColourRGBA(co | (0xffu << 24)); |
190 | } |
191 | |
192 | constexpr int AsInteger() const noexcept { |
193 | return co; |
194 | } |
195 | |
196 | constexpr int OpaqueRGB() const noexcept { |
197 | return co & 0xffffff; |
198 | } |
199 | |
200 | // Red, green and blue values as bytes 0..255 |
201 | constexpr unsigned char GetRed() const noexcept { |
202 | return co & 0xff; |
203 | } |
204 | constexpr unsigned char GetGreen() const noexcept { |
205 | return (co >> 8) & 0xff; |
206 | } |
207 | constexpr unsigned char GetBlue() const noexcept { |
208 | return (co >> 16) & 0xff; |
209 | } |
210 | constexpr unsigned char GetAlpha() const noexcept { |
211 | return (co >> 24) & 0xff; |
212 | } |
213 | |
214 | // Red, green, blue, and alpha values as float 0..1.0 |
215 | constexpr float GetRedComponent() const noexcept { |
216 | return GetRed() / componentMaximum; |
217 | } |
218 | constexpr float GetGreenComponent() const noexcept { |
219 | return GetGreen() / componentMaximum; |
220 | } |
221 | constexpr float GetBlueComponent() const noexcept { |
222 | return GetBlue() / componentMaximum; |
223 | } |
224 | constexpr float GetAlphaComponent() const noexcept { |
225 | return GetAlpha() / componentMaximum; |
226 | } |
227 | |
228 | constexpr bool operator==(const ColourRGBA &other) const noexcept { |
229 | return co == other.co; |
230 | } |
231 | |
232 | constexpr bool IsOpaque() const noexcept { |
233 | return GetAlpha() == 0xff; |
234 | } |
235 | |
236 | ColourRGBA MixedWith(ColourRGBA other) const noexcept; |
237 | ColourRGBA MixedWith(ColourRGBA other, double proportion) const noexcept; |
238 | }; |
239 | |
240 | /** |
241 | * Holds an RGBA colour and stroke width to stroke a shape. |
242 | */ |
243 | class Stroke { |
244 | public: |
245 | ColourRGBA colour; |
246 | XYPOSITION width; |
247 | constexpr Stroke(ColourRGBA colour_, XYPOSITION width_=1.0) noexcept : |
248 | colour(colour_), width(width_) { |
249 | } |
250 | constexpr float WidthF() const noexcept { |
251 | return static_cast<float>(width); |
252 | } |
253 | }; |
254 | |
255 | /** |
256 | * Holds an RGBA colour to fill a shape. |
257 | */ |
258 | class Fill { |
259 | public: |
260 | ColourRGBA colour; |
261 | constexpr Fill(ColourRGBA colour_) noexcept : |
262 | colour(colour_) { |
263 | } |
264 | }; |
265 | |
266 | /** |
267 | * Holds a pair of RGBA colours and stroke width to fill and stroke a shape. |
268 | */ |
269 | class FillStroke { |
270 | public: |
271 | Fill fill; |
272 | Stroke stroke; |
273 | constexpr FillStroke(ColourRGBA colourFill_, ColourRGBA colourStroke_, XYPOSITION widthStroke_=1.0) noexcept : |
274 | fill(colourFill_), stroke(colourStroke_, widthStroke_) { |
275 | } |
276 | constexpr FillStroke(ColourRGBA colourBoth, XYPOSITION widthStroke_=1.0) noexcept : |
277 | fill(colourBoth), stroke(colourBoth, widthStroke_) { |
278 | } |
279 | }; |
280 | |
281 | /** |
282 | * Holds an element of a gradient with an RGBA colour and a relative position. |
283 | */ |
284 | class ColourStop { |
285 | public: |
286 | XYPOSITION position; |
287 | ColourRGBA colour; |
288 | constexpr ColourStop(XYPOSITION position_, ColourRGBA colour_) noexcept : |
289 | position(position_), colour(colour_) { |
290 | } |
291 | }; |
292 | |
293 | } |
294 | |
295 | #endif |
296 | |