| 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 | |