diff options
| author | Neil <nyamatongwe@gmail.com> | 2021-03-19 13:43:32 +1100 | 
|---|---|---|
| committer | Neil <nyamatongwe@gmail.com> | 2021-03-19 13:43:32 +1100 | 
| commit | 9659ea34246320bd9872740d1ee6a5c9ae9066c6 (patch) | |
| tree | cf185d19f133dd40a7faed0bd24c1a04fb12a1be /src | |
| parent | 63fbfcb7c05dc7035ebf80805be70b0109def792 (diff) | |
| download | scintilla-mirror-9659ea34246320bd9872740d1ee6a5c9ae9066c6.tar.gz | |
Add Geometry.cxx for geometric and colour operations too complex for headers.
Add FillStroke to hold parameters for drawing shapes.
Diffstat (limited to 'src')
| -rw-r--r-- | src/Geometry.cxx | 97 | ||||
| -rw-r--r-- | src/Geometry.h | 101 | 
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_) {  	}  };  | 
