aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Geometry.cxx97
-rw-r--r--src/Geometry.h101
2 files changed, 191 insertions, 7 deletions
diff --git a/src/Geometry.cxx b/src/Geometry.cxx
new file mode 100644
index 000000000..68ce1f628
--- /dev/null
+++ b/src/Geometry.cxx
@@ -0,0 +1,97 @@
+// Scintilla source code edit control
+/** @file Geometry.cxx
+ ** Helper functions for geometric calculations.
+ **/
+// Copyright 2020 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <cmath>
+
+#include <algorithm>
+
+#include "Geometry.h"
+
+namespace Scintilla {
+
+PRectangle Clamp(PRectangle rc, Edge edge, XYPOSITION position) noexcept {
+ switch (edge) {
+ case Edge::left:
+ return PRectangle(std::clamp(position, rc.left, rc.right), rc.top, rc.right, rc.bottom);
+ case Edge::top:
+ return PRectangle(rc.left, std::clamp(position, rc.top, rc.bottom), rc.right, rc.bottom);
+ case Edge::right:
+ return PRectangle(rc.left, rc.top, std::clamp(position, rc.left, rc.right), rc.bottom);
+ case Edge::bottom:
+ default:
+ return PRectangle(rc.left, rc.top, rc.right, std::clamp(position, rc.top, rc.bottom));
+ }
+}
+
+PRectangle Side(PRectangle rc, Edge edge, XYPOSITION size) noexcept {
+ switch (edge) {
+ case Edge::left:
+ return PRectangle(rc.left, rc.top, std::min(rc.left + size, rc.right), rc.bottom);
+ case Edge::top:
+ return PRectangle(rc.left, rc.top, rc.right, std::min(rc.top + size, rc.bottom));
+ case Edge::right:
+ return PRectangle(std::max(rc.left, rc.right - size), rc.top, rc.right, rc.bottom);
+ case Edge::bottom:
+ default:
+ return PRectangle(rc.left, std::max(rc.top, rc.bottom - size), rc.right, rc.bottom);
+ }
+}
+
+Interval Intersection(Interval a, Interval b) noexcept {
+ const XYPOSITION leftMax = std::max(a.left, b.left);
+ const XYPOSITION rightMin = std::min(a.right, b.right);
+ // If the result would have a negative width. make empty instead.
+ const XYPOSITION rightResult = (rightMin >= leftMax) ? rightMin : leftMax;
+ return { leftMax, rightResult };
+}
+
+PRectangle Intersection(PRectangle rc, Interval horizontalBounds) noexcept {
+ const Interval intersection = Intersection(HorizontalBounds(rc), horizontalBounds);
+ return PRectangle(intersection.left, rc.top, intersection.right, rc.bottom);
+}
+
+Interval HorizontalBounds(PRectangle rc) noexcept {
+ return { rc.left, rc.right };
+}
+
+XYPOSITION PixelAlign(XYPOSITION xy, int pixelDivisions) noexcept {
+ return std::round(xy * pixelDivisions) / pixelDivisions;
+}
+
+XYPOSITION PixelAlignFloor(XYPOSITION xy, int pixelDivisions) noexcept {
+ return std::floor(xy * pixelDivisions) / pixelDivisions;
+}
+
+Point PixelAlign(const Point &pt, int pixelDivisions) noexcept {
+ return Point(
+ std::round(pt.x * pixelDivisions) / pixelDivisions,
+ std::round(pt.y * pixelDivisions) / pixelDivisions);
+}
+
+PRectangle PixelAlign(const PRectangle &rc, int pixelDivisions) noexcept {
+ // Move left and right side to nearest pixel to avoid blurry visuals.
+ // The top and bottom should be integers but floor them to make sure.
+ // `pixelDivisions` is commonly 1 except for 'retina' displays where it is 2.
+ // On retina displays, the positions should be moved to the nearest device
+ // pixel which is the nearest half logical pixel.
+ return PRectangle(
+ std::round(rc.left * pixelDivisions) / pixelDivisions,
+ PixelAlignFloor(rc.top, pixelDivisions),
+ std::round(rc.right * pixelDivisions) / pixelDivisions,
+ PixelAlignFloor(rc.bottom, pixelDivisions));
+}
+
+PRectangle PixelAlignOutside(const PRectangle &rc, int pixelDivisions) noexcept {
+ // Move left and right side to extremes (floor(left) ceil(right)) to avoid blurry visuals.
+ return PRectangle(
+ std::floor(rc.left * pixelDivisions) / pixelDivisions,
+ std::floor(rc.top * pixelDivisions) / pixelDivisions,
+ std::ceil(rc.right * pixelDivisions) / pixelDivisions,
+ std::floor(rc.bottom * pixelDivisions) / pixelDivisions);
+}
+
+}
diff --git a/src/Geometry.h b/src/Geometry.h
index 35702a468..bab50d502 100644
--- a/src/Geometry.h
+++ b/src/Geometry.h
@@ -35,6 +35,10 @@ public:
return Point(static_cast<XYPOSITION>(x_), static_cast<XYPOSITION>(y_));
}
+ constexpr bool operator==(Point other) const noexcept {
+ return (x == other.x) && (y == other.y);
+ }
+
constexpr bool operator!=(Point other) const noexcept {
return (x != other.x) || (y != other.y);
}
@@ -50,9 +54,24 @@ public:
// Other automatically defined methods (assignment, copy constructor, destructor) are fine
};
-struct Interval {
+
+/**
+ * A geometric interval class.
+ */
+class Interval {
+public:
XYPOSITION left;
XYPOSITION right;
+ constexpr bool operator==(const Interval &other) const noexcept {
+ return (left == other.left) && (right == other.right);
+ }
+ constexpr XYPOSITION Width() const noexcept { return right - left; }
+ constexpr bool Empty() const noexcept {
+ return Width() <= 0;
+ }
+ constexpr bool Intersects(Interval other) const noexcept {
+ return (right > other.left) && (left < other.right);
+ }
};
/**
@@ -105,6 +124,15 @@ public:
right += xDelta;
bottom += yDelta;
}
+
+ constexpr PRectangle Inset(XYPOSITION delta) const noexcept {
+ return PRectangle(left + delta, top + delta, right - delta, bottom - delta);
+ }
+
+ constexpr Point Centre() const noexcept {
+ return Point((left + right) / 2, (top + bottom) / 2);
+ }
+
constexpr XYPOSITION Width() const noexcept { return right - left; }
constexpr XYPOSITION Height() const noexcept { return bottom - top; }
constexpr bool Empty() const noexcept {
@@ -112,6 +140,23 @@ public:
}
};
+enum class Edge { left, top, bottom, right };
+
+PRectangle Clamp(PRectangle rc, Edge edge, XYPOSITION position) noexcept;
+PRectangle Side(PRectangle rc, Edge edge, XYPOSITION size) noexcept;
+
+Interval Intersection(Interval a, Interval b) noexcept;
+PRectangle Intersection(PRectangle rc, Interval horizontalBounds) noexcept;
+Interval HorizontalBounds(PRectangle rc) noexcept;
+
+XYPOSITION PixelAlign(XYPOSITION xy, int pixelDivisions) noexcept;
+XYPOSITION PixelAlignFloor(XYPOSITION xy, int pixelDivisions) noexcept;
+
+Point PixelAlign(const Point &pt, int pixelDivisions) noexcept;
+
+PRectangle PixelAlign(const PRectangle &rc, int pixelDivisions) noexcept;
+PRectangle PixelAlignOutside(const PRectangle &rc, int pixelDivisions) noexcept;
+
/**
* Holds an RGB colour with 8 bits for each component.
*/
@@ -165,11 +210,7 @@ public:
constexpr explicit ColourAlpha(int co_ = 0) noexcept : ColourDesired(co_) {
}
- constexpr ColourAlpha(unsigned int red, unsigned int green, unsigned int blue) noexcept :
- ColourDesired(red | (green << 8) | (blue << 16)) {
- }
-
- constexpr ColourAlpha(unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha) noexcept :
+ constexpr ColourAlpha(unsigned int red, unsigned int green, unsigned int blue, unsigned int alpha=0xff) noexcept :
ColourDesired(red | (green << 8) | (blue << 16) | (alpha << 24)) {
}
@@ -177,6 +218,10 @@ public:
ColourDesired(cd.AsInteger() | (alpha << 24)) {
}
+ constexpr ColourAlpha(ColourDesired cd) noexcept :
+ ColourDesired(cd.AsInteger() | (0xff << 24)) {
+ }
+
constexpr ColourDesired GetColour() const noexcept {
return ColourDesired(AsInteger() & 0xffffff);
}
@@ -189,6 +234,10 @@ public:
return GetAlpha() / componentMaximum;
}
+ constexpr bool IsOpaque() const noexcept {
+ return GetAlpha() == 0xff;
+ }
+
constexpr ColourAlpha MixedWith(ColourAlpha other) const noexcept {
const unsigned int red = (GetRed() + other.GetRed()) / 2;
const unsigned int green = (GetGreen() + other.GetGreen()) / 2;
@@ -199,13 +248,51 @@ public:
};
/**
+* Holds an RGBA colour and stroke width to stroke a shape.
+*/
+class Stroke {
+public:
+ ColourAlpha colourStroke;
+ XYPOSITION widthStroke;
+ constexpr Stroke(ColourAlpha colourStroke_, XYPOSITION widthStroke_=1.0f) noexcept :
+ colourStroke(colourStroke_), widthStroke(widthStroke_) {
+ }
+};
+
+/**
+* Holds an RGBA colour to fill a shape.
+*/
+class Fill {
+public:
+ ColourAlpha colourFill;
+ constexpr Fill(ColourAlpha colourFill_) noexcept :
+ colourFill(colourFill_) {
+ }
+};
+
+/**
+* Holds a pair of RGBA colours and stroke width to fill and stroke a shape.
+*/
+class FillStroke {
+public:
+ Fill fill;
+ Stroke stroke;
+ constexpr FillStroke(ColourAlpha colourFill_, ColourAlpha colourStroke_, XYPOSITION widthStroke_=1.0f) noexcept :
+ fill(colourFill_), stroke(colourStroke_, widthStroke_) {
+ }
+ constexpr FillStroke(ColourAlpha colourBoth, XYPOSITION widthStroke_=1.0f) noexcept :
+ fill(colourBoth), stroke(colourBoth, widthStroke_) {
+ }
+};
+
+/**
* Holds an element of a gradient with an RGBA colour and a relative position.
*/
class ColourStop {
public:
float position;
ColourAlpha colour;
- ColourStop(float position_, ColourAlpha colour_) noexcept :
+ constexpr ColourStop(float position_, ColourAlpha colour_) noexcept :
position(position_), colour(colour_) {
}
};