1 | // Scintilla source code edit control |
2 | /** @file Geometry.cxx |
3 | ** Helper functions for geometric 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 | #include <cstdint> |
9 | #include <cmath> |
10 | |
11 | #include <algorithm> |
12 | |
13 | #include "Geometry.h" |
14 | |
15 | namespace { |
16 | |
17 | constexpr unsigned int Mixed(unsigned char a, unsigned char b, double proportion) noexcept { |
18 | return static_cast<unsigned int>(a + proportion * (b - a)); |
19 | } |
20 | |
21 | } |
22 | |
23 | namespace Scintilla::Internal { |
24 | |
25 | PRectangle Clamp(PRectangle rc, Edge edge, XYPOSITION position) noexcept { |
26 | switch (edge) { |
27 | case Edge::left: |
28 | return PRectangle(std::clamp(position, rc.left, rc.right), rc.top, rc.right, rc.bottom); |
29 | case Edge::top: |
30 | return PRectangle(rc.left, std::clamp(position, rc.top, rc.bottom), rc.right, rc.bottom); |
31 | case Edge::right: |
32 | return PRectangle(rc.left, rc.top, std::clamp(position, rc.left, rc.right), rc.bottom); |
33 | case Edge::bottom: |
34 | default: |
35 | return PRectangle(rc.left, rc.top, rc.right, std::clamp(position, rc.top, rc.bottom)); |
36 | } |
37 | } |
38 | |
39 | PRectangle Side(PRectangle rc, Edge edge, XYPOSITION size) noexcept { |
40 | switch (edge) { |
41 | case Edge::left: |
42 | return PRectangle(rc.left, rc.top, std::min(rc.left + size, rc.right), rc.bottom); |
43 | case Edge::top: |
44 | return PRectangle(rc.left, rc.top, rc.right, std::min(rc.top + size, rc.bottom)); |
45 | case Edge::right: |
46 | return PRectangle(std::max(rc.left, rc.right - size), rc.top, rc.right, rc.bottom); |
47 | case Edge::bottom: |
48 | default: |
49 | return PRectangle(rc.left, std::max(rc.top, rc.bottom - size), rc.right, rc.bottom); |
50 | } |
51 | } |
52 | |
53 | Interval Intersection(Interval a, Interval b) noexcept { |
54 | const XYPOSITION leftMax = std::max(a.left, b.left); |
55 | const XYPOSITION rightMin = std::min(a.right, b.right); |
56 | // If the result would have a negative width. make empty instead. |
57 | const XYPOSITION rightResult = (rightMin >= leftMax) ? rightMin : leftMax; |
58 | return { leftMax, rightResult }; |
59 | } |
60 | |
61 | PRectangle Intersection(PRectangle rc, Interval horizontalBounds) noexcept { |
62 | const Interval intersection = Intersection(HorizontalBounds(rc), horizontalBounds); |
63 | return PRectangle(intersection.left, rc.top, intersection.right, rc.bottom); |
64 | } |
65 | |
66 | Interval HorizontalBounds(PRectangle rc) noexcept { |
67 | return { rc.left, rc.right }; |
68 | } |
69 | |
70 | XYPOSITION PixelAlign(XYPOSITION xy, int pixelDivisions) noexcept { |
71 | return std::round(xy * pixelDivisions) / pixelDivisions; |
72 | } |
73 | |
74 | XYPOSITION PixelAlignFloor(XYPOSITION xy, int pixelDivisions) noexcept { |
75 | return std::floor(xy * pixelDivisions) / pixelDivisions; |
76 | } |
77 | |
78 | Point PixelAlign(const Point &pt, int pixelDivisions) noexcept { |
79 | return Point( |
80 | std::round(pt.x * pixelDivisions) / pixelDivisions, |
81 | std::round(pt.y * pixelDivisions) / pixelDivisions); |
82 | } |
83 | |
84 | PRectangle PixelAlign(const PRectangle &rc, int pixelDivisions) noexcept { |
85 | // Move left and right side to nearest pixel to avoid blurry visuals. |
86 | // The top and bottom should be integers but floor them to make sure. |
87 | // `pixelDivisions` is commonly 1 except for 'retina' displays where it is 2. |
88 | // On retina displays, the positions should be moved to the nearest device |
89 | // pixel which is the nearest half logical pixel. |
90 | return PRectangle( |
91 | std::round(rc.left * pixelDivisions) / pixelDivisions, |
92 | PixelAlignFloor(rc.top, pixelDivisions), |
93 | std::round(rc.right * pixelDivisions) / pixelDivisions, |
94 | PixelAlignFloor(rc.bottom, pixelDivisions)); |
95 | } |
96 | |
97 | PRectangle PixelAlignOutside(const PRectangle &rc, int pixelDivisions) noexcept { |
98 | // Move left and right side to extremes (floor(left) ceil(right)) to avoid blurry visuals. |
99 | return PRectangle( |
100 | std::floor(rc.left * pixelDivisions) / pixelDivisions, |
101 | std::floor(rc.top * pixelDivisions) / pixelDivisions, |
102 | std::ceil(rc.right * pixelDivisions) / pixelDivisions, |
103 | std::floor(rc.bottom * pixelDivisions) / pixelDivisions); |
104 | } |
105 | |
106 | ColourRGBA ColourRGBA::MixedWith(ColourRGBA other) const noexcept { |
107 | const unsigned int red = (GetRed() + other.GetRed()) / 2; |
108 | const unsigned int green = (GetGreen() + other.GetGreen()) / 2; |
109 | const unsigned int blue = (GetBlue() + other.GetBlue()) / 2; |
110 | const unsigned int alpha = (GetAlpha() + other.GetAlpha()) / 2; |
111 | return ColourRGBA(red, green, blue, alpha); |
112 | } |
113 | |
114 | ColourRGBA ColourRGBA::MixedWith(ColourRGBA other, double proportion) const noexcept { |
115 | return ColourRGBA( |
116 | Mixed(GetRed(), other.GetRed(), proportion), |
117 | Mixed(GetGreen(), other.GetGreen(), proportion), |
118 | Mixed(GetBlue(), other.GetBlue(), proportion), |
119 | Mixed(GetAlpha(), other.GetAlpha(), proportion)); |
120 | } |
121 | |
122 | } |
123 | |