Browse Source

Add Rectangle class

Michael Ragazzon 3 years ago
parent
commit
5267e7792f

+ 1 - 0
CMake/FileList.cmake

@@ -189,6 +189,7 @@ set(Core_PUB_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertyIdSet.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertyParser.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertySpecification.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Rectangle.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/RenderInterface.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ScriptInterface.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ScrollTypes.h

+ 1 - 1
Include/RmlUi/Core/Elements/ElementProgress.h

@@ -111,7 +111,7 @@ private:
 	Texture texture;
 
 	// The rectangle extracted from a sprite, 'rect_set' controls whether it is active.
-	Rectangle rect;
+	Rectanglef rect;
 	bool rect_set;
 
 	// The geometry used to render this element. Only applies if the 'fill-image' property is set.

+ 125 - 0
Include/RmlUi/Core/Rectangle.h

@@ -0,0 +1,125 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ */
+
+#ifndef RMLUI_CORE_RECTANGLE_H
+#define RMLUI_CORE_RECTANGLE_H
+
+#include "Debug.h"
+#include "Vector2.h"
+
+namespace Rml {
+
+/**
+    Templated class for a generic rectangle.
+ */
+template <typename Type>
+class Rectangle {
+public:
+	using Vector2Type = Vector2<Type>;
+
+	Rectangle() = default;
+
+	static inline Rectangle FromPosition(Vector2Type pos) { return Rectangle(pos, pos); }
+	static inline Rectangle FromPositionSize(Vector2Type pos, Vector2Type size) { return Rectangle(pos, pos + size); }
+	static inline Rectangle FromSize(Vector2Type size) { return Rectangle(Vector2Type(), size); }
+	static inline Rectangle FromCorners(Vector2Type top_left, Vector2Type bottom_right) { return Rectangle(top_left, bottom_right); }
+	static inline Rectangle MakeInvalid() { return Rectangle(Vector2Type(0), Vector2Type(-1)); }
+
+	Vector2Type Position() const { return p0; }
+	Vector2Type Size() const { return p1 - p0; }
+
+	Vector2Type TopLeft() const { return p0; }
+	Vector2Type BottomRight() const { return p1; }
+
+	Vector2Type Center() const { return (p0 + p1) / Type(2); }
+
+	Type Left() const { return p0.x; }
+	Type Right() const { return p1.x; }
+	Type Top() const { return p0.y; }
+	Type Bottom() const { return p1.y; }
+	Type Width() const { return p1.x - p0.x; }
+	Type Height() const { return p1.y - p0.y; }
+
+	void Extend(Type v) { Extend(Vector2Type(v)); }
+	void Extend(Vector2Type v)
+	{
+		p0 -= v;
+		p1 += v;
+	}
+	void ExtendTopLeft(Vector2Type v) { p0 -= v; }
+	void ExtendBottomRight(Vector2Type v) { p1 += v; }
+
+	void Join(Vector2Type p)
+	{
+		p0 = Math::Min(p0, p);
+		p1 = Math::Max(p1, p);
+	}
+	void Join(Rectangle other)
+	{
+		p0 = Math::Min(p0, other.p0);
+		p1 = Math::Max(p1, other.p1);
+	}
+
+	void Intersect(Rectangle other)
+	{
+		RMLUI_ASSERT(Valid() && other.Valid());
+		p0 = Math::Max(p0, other.p0);
+		p1 = Math::Max(Math::Min(p1, other.p1), p0);
+	}
+	void IntersectIfValid(Rectangle other)
+	{
+		if (!Valid())
+			*this = other;
+		else if (other.Valid())
+			Intersect(other);
+	}
+
+	bool Intersects(Rectangle other) const { return p0.x < other.p1.x && p1.x > other.p0.x && p0.y < other.p1.y && p1.y > other.p0.y; }
+	bool Contains(Vector2Type point) const { return point.x >= p0.x && point.x <= p1.x && point.y >= p0.y && point.y <= p1.y; }
+
+	bool Valid() const { return p0.x <= p1.x && p0.y <= p1.y; }
+
+	bool operator==(Rectangle rhs) const { return p0 == rhs.p0 && p1 == rhs.p1; }
+	bool operator!=(Rectangle rhs) const { return !(*this == rhs); }
+
+	template <typename U>
+	explicit operator Rectangle<U>() const
+	{
+		return Rectangle<U>::FromCorners(static_cast<Vector2<U>>(p0), static_cast<Vector2<U>>(p1));
+	}
+
+	Vector2Type p0; // Minimum coordinates for a valid rectangle.
+	Vector2Type p1; // Maximum coordinates for a valid rectangle.
+
+private:
+	Rectangle(Vector2Type p0, Vector2Type p1) : p0(p0), p1(p1) {}
+};
+
+} // namespace Rml
+
+#endif

+ 2 - 8
Include/RmlUi/Core/Spritesheet.h

@@ -35,14 +35,8 @@ namespace Rml {
 
 struct Spritesheet;
 
-
-struct Rectangle {
-	Rectangle(float x = 0, float y = 0, float width = 0, float height = 0) : x(x), y(y), width(width), height(height) {}
-	float x, y, width, height;
-};
-
 struct Sprite {
-	Rectangle rectangle; // in 'px' units
+	Rectanglef rectangle; // in 'px' units
 	const Spritesheet* sprite_sheet;
 };
 using SpriteMap = UnorderedMap<String, Sprite>; // key: sprite name (as given in @spritesheet)
@@ -63,7 +57,7 @@ struct Spritesheet {
 		int definition_line_number, float display_scale, const Texture& texture);
 };
 
-using SpriteDefinitionList = Vector<Pair<String, Rectangle>>; // Sprite name and rectangle
+using SpriteDefinitionList = Vector<Pair<String, Rectanglef>>; // Sprite name and rectangle
 
 
 /**

+ 3 - 1
Include/RmlUi/Core/Types.h

@@ -52,6 +52,7 @@ enum class Character : char32_t { Null, Replacement = 0xfffd };
 #include "Vector2.h"
 #include "Vector3.h"
 #include "Vector4.h"
+#include "Rectangle.h"
 #include "Matrix4.h"
 #include "ObserverPtr.h"
 
@@ -67,6 +68,8 @@ using Vector3i = Vector3< int >;
 using Vector3f = Vector3< float >;
 using Vector4i = Vector4< int >;
 using Vector4f = Vector4< float >;
+using Rectanglei = Rectangle< int >;
+using Rectanglef = Rectangle< float >;
 using ColumnMajorMatrix4f = Matrix4< float, ColumnMajorStorage< float > >;
 using RowMajorMatrix4f = Matrix4< float, RowMajorStorage< float > >;
 using Matrix4f = RMLUI_MATRIX4_TYPE;
@@ -87,7 +90,6 @@ struct Animation;
 struct Transition;
 struct TransitionList;
 struct DecoratorDeclarationList;
-struct Rectangle;
 enum class EventId : uint16_t;
 enum class PropertyId : uint8_t;
 enum class MediaQueryId : uint8_t;

+ 7 - 6
Source/Core/DecoratorNinePatch.cpp

@@ -43,7 +43,7 @@ DecoratorNinePatch::~DecoratorNinePatch()
 {
 }
 
-bool DecoratorNinePatch::Initialise(const Rectangle& _rect_outer, const Rectangle& _rect_inner, const Array<Property, 4>* _edges, const Texture& _texture, float _display_scale)
+bool DecoratorNinePatch::Initialise(const Rectanglef& _rect_outer, const Rectanglef& _rect_inner, const Array<Property, 4>* _edges, const Texture& _texture, float _display_scale)
 {
 	rect_outer = _rect_outer;
 	rect_inner = _rect_inner;
@@ -79,11 +79,12 @@ DecoratorDataHandle DecoratorNinePatch::GenerateElementData(Element* element) co
 	/* In the following, we operate on the four diagonal vertices in the grid, as they define the whole grid. */
 
 	// Absolute texture coordinates 'px'
-	Vector2f tex_pos[4];
-	tex_pos[0] = { rect_outer.x,                    rect_outer.y };
-	tex_pos[1] = { rect_inner.x,                    rect_inner.y };
-	tex_pos[2] = { rect_inner.x + rect_inner.width, rect_inner.y + rect_inner.height };
-	tex_pos[3] = { rect_outer.x + rect_outer.width, rect_outer.y + rect_outer.height };
+	Vector2f tex_pos[4] = {
+		rect_outer.TopLeft(),
+		rect_inner.TopLeft(),
+		rect_inner.BottomRight(),
+		rect_outer.BottomRight(),
+	};
 
 	// Normalized texture coordinates [0, 1]
 	Vector2f tex_coords[4];

+ 2 - 2
Source/Core/DecoratorNinePatch.h

@@ -42,7 +42,7 @@ public:
 	DecoratorNinePatch();
 	virtual ~DecoratorNinePatch();
 
-	bool Initialise(const Rectangle& rect_outer, const Rectangle& rect_inner, const Array<Property, 4>* _edges, const Texture& texture, float display_scale);
+	bool Initialise(const Rectanglef& rect_outer, const Rectanglef& rect_inner, const Array<Property, 4>* _edges, const Texture& texture, float display_scale);
 
 	DecoratorDataHandle GenerateElementData(Element* element) const override;
 	void ReleaseElementData(DecoratorDataHandle element_data) const override;
@@ -50,7 +50,7 @@ public:
 	void RenderElement(Element* element, DecoratorDataHandle element_data) const override;
 
 private:
-	Rectangle rect_outer, rect_inner;
+	Rectanglef rect_outer, rect_inner;
 	float display_scale = 1;
 	UniquePtr<Array<Property,4>> edges;
 };

+ 2 - 4
Source/Core/DecoratorTiledInstancer.cpp

@@ -110,10 +110,8 @@ bool DecoratorTiledInstancer::GetTileProperties(DecoratorTiled::Tile* tiles, Tex
 		// A tile is always either a sprite or an image.
 		if (const Sprite * sprite = instancer_interface.GetSprite(texture_name))
 		{
-			tile.position.x = sprite->rectangle.x;
-			tile.position.y = sprite->rectangle.y;
-			tile.size.x = sprite->rectangle.width;
-			tile.size.y = sprite->rectangle.height;
+			tile.position = sprite->rectangle.Position();
+			tile.size = sprite->rectangle.Size();
 			tile.display_scale = sprite->sprite_sheet->display_scale;
 
 			texture = sprite->sprite_sheet->texture;

+ 9 - 18
Source/Core/Elements/ElementImage.cpp

@@ -59,11 +59,11 @@ bool ElementImage::GetIntrinsicDimensions(Vector2f& _dimensions, float& _ratio)
 
 	// Calculate the x dimension.
 	if (HasAttribute("width"))
-		dimensions.x = GetAttribute< float >("width", -1);
+		dimensions.x = GetAttribute<float>("width", -1);
 	else if (rect_source == RectSource::None)
 		dimensions.x = (float)texture.GetDimensions(GetRenderInterface()).x;
 	else
-		dimensions.x = rect.width;
+		dimensions.x = rect.Width();
 
 	// Calculate the y dimension.
 	if (HasAttribute("height"))
@@ -71,7 +71,7 @@ bool ElementImage::GetIntrinsicDimensions(Vector2f& _dimensions, float& _ratio)
 	else if (rect_source == RectSource::None)
 		dimensions.y = (float)texture.GetDimensions(GetRenderInterface()).y;
 	else
-		dimensions.y = rect.height;
+		dimensions.y = rect.Height();
 
 	dimensions *= dimensions_scale;
 
@@ -190,17 +190,9 @@ void ElementImage::GenerateGeometry()
 	Vector2f texcoords[2];
 	if (rect_source != RectSource::None)
 	{
-		Vector2f texture_dimensions((float) texture.GetDimensions(GetRenderInterface()).x, (float) texture.GetDimensions(GetRenderInterface()).y);
-		if (texture_dimensions.x == 0)
-			texture_dimensions.x = 1;
-		if (texture_dimensions.y == 0)
-			texture_dimensions.y = 1;
-
-		texcoords[0].x = rect.x / texture_dimensions.x;
-		texcoords[0].y = rect.y / texture_dimensions.y;
-
-		texcoords[1].x = (rect.x + rect.width) / texture_dimensions.x;
-		texcoords[1].y = (rect.y + rect.height) / texture_dimensions.y;
+		Vector2f texture_dimensions = Vector2f(Math::Max(texture.GetDimensions(GetRenderInterface()), Vector2i(1)));
+		texcoords[0] = rect.TopLeft() / texture_dimensions;
+		texcoords[1] = rect.BottomRight() / texture_dimensions;
 	}
 	else
 	{
@@ -305,10 +297,9 @@ void ElementImage::UpdateRect()
 			}
 			else
 			{
-				rect.x = (float)std::atof(coords_list[0].c_str());
-				rect.y = (float)std::atof(coords_list[1].c_str());
-				rect.width = (float)std::atof(coords_list[2].c_str());
-				rect.height = (float)std::atof(coords_list[3].c_str());
+				const Vector2f position = {FromString(coords_list[0], 0.f), FromString(coords_list[1], 0.f)};
+				const Vector2f size = {FromString(coords_list[2], 0.f), FromString(coords_list[3], 0.f)};
+				rect = Rectanglef::FromPositionSize(position, size);
 
 				// We have new, valid coordinates; force the geometry to be regenerated.
 				valid_rect = true;

+ 1 - 1
Source/Core/Elements/ElementImage.h

@@ -119,7 +119,7 @@ private:
 
 	// The rectangle extracted from the sprite or 'rect' attribute. The rect_source will be None if
 	// these have not been specified or are invalid.
-	Rectangle rect;
+	Rectanglef rect;
 	enum class RectSource { None, Attribute, Sprite } rect_source;
 
 	// The geometry used to render this element.

+ 3 - 11
Source/Core/Elements/ElementProgress.cpp

@@ -239,17 +239,9 @@ void ElementProgress::GenerateGeometry()
 	Vector2f texcoords[2];
 	if (rect_set)
 	{
-		Vector2f texture_dimensions((float)texture.GetDimensions(GetRenderInterface()).x, (float)texture.GetDimensions(GetRenderInterface()).y);
-		if (texture_dimensions.x == 0)
-			texture_dimensions.x = 1;
-		if (texture_dimensions.y == 0)
-			texture_dimensions.y = 1;
-
-		texcoords[0].x = rect.x / texture_dimensions.x;
-		texcoords[0].y = rect.y / texture_dimensions.y;
-
-		texcoords[1].x = (rect.x + rect.width) / texture_dimensions.x;
-		texcoords[1].y = (rect.y + rect.height) / texture_dimensions.y;
+		Vector2f texture_dimensions = Vector2f(Math::Max(texture.GetDimensions(GetRenderInterface()), Vector2i(1)));
+		texcoords[0] = rect.TopLeft() / texture_dimensions;
+		texcoords[1] = rect.BottomRight() / texture_dimensions;
 	}
 	else
 	{

+ 1 - 1
Source/Core/Spritesheet.cpp

@@ -55,7 +55,7 @@ bool SpritesheetList::AddSpriteSheet(const String& name, const String& image_sou
 	for (auto& sprite_definition : sprite_definitions)
 	{
 		const String& sprite_name = sprite_definition.first;
-		const Rectangle& sprite_rectangle = sprite_definition.second;
+		const Rectanglef& sprite_rectangle = sprite_definition.second;
 
 		Sprite& new_sprite = sprite_map[sprite_name];
 		if (new_sprite.sprite_sheet)

+ 6 - 6
Source/Core/StyleSheetParser.cpp

@@ -142,17 +142,17 @@ public:
 			if (!specification.ParseShorthandDeclaration(properties, id_rectangle, value))
 				return false;
 
-			Rectangle rectangle;
+			Vector2f position, size;
 			if (auto property = properties.GetProperty(id_rx))
-				rectangle.x = ComputeAbsoluteLength(*property, 1.f, Vector2f(1.f));
+				position.x = ComputeAbsoluteLength(*property, 1.f, Vector2f(1.f));
 			if (auto property = properties.GetProperty(id_ry))
-				rectangle.y = ComputeAbsoluteLength(*property, 1.f, Vector2f(1.f));
+				position.y = ComputeAbsoluteLength(*property, 1.f, Vector2f(1.f));
 			if (auto property = properties.GetProperty(id_rw))
-				rectangle.width = ComputeAbsoluteLength(*property, 1.f, Vector2f(1.f));
+				size.x = ComputeAbsoluteLength(*property, 1.f, Vector2f(1.f));
 			if (auto property = properties.GetProperty(id_rh))
-				rectangle.height = ComputeAbsoluteLength(*property, 1.f, Vector2f(1.f));
+				size.y = ComputeAbsoluteLength(*property, 1.f, Vector2f(1.f));
 
-			sprite_definitions.emplace_back(name, rectangle);
+			sprite_definitions.emplace_back(name, Rectanglef::FromPositionSize(position, size));
 		}
 
 		return true;