From 5f13562dbd7a63bda1f9c538e372cf10a3acde42 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Mon, 19 Sep 2022 15:53:42 +0100 Subject: [PATCH 01/10] Started work on Geometry2D Utility library --- utilities/olcUTIL_Geometry2D.h | 996 +++++++++++++++++++++++++++++++++ 1 file changed, 996 insertions(+) create mode 100644 utilities/olcUTIL_Geometry2D.h diff --git a/utilities/olcUTIL_Geometry2D.h b/utilities/olcUTIL_Geometry2D.h new file mode 100644 index 0000000..21f50c1 --- /dev/null +++ b/utilities/olcUTIL_Geometry2D.h @@ -0,0 +1,996 @@ +/* + OneLoneCoder - Geometry 2D v1.00 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + A collection of 2D Geometric primitives and functions to work with + and between them. + + + License (OLC-3) + ~~~~~~~~~~~~~~~ + + Copyright 2018 - 2022 OneLoneCoder.com + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions or derivations of source code must retain the above + copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions or derivative works in binary form must reproduce + the above copyright notice. This list of conditions and the following + disclaimer must be reproduced in the documentation and/or other + materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Links + ~~~~~ + YouTube: https://www.youtube.com/javidx9 + Discord: https://discord.gg/WhwHUMV + Twitter: https://www.twitter.com/javidx9 + Twitch: https://www.twitch.tv/javidx9 + GitHub: https://www.github.com/onelonecoder + Homepage: https://www.onelonecoder.com + + Author + ~~~~~~ + David Barr, aka javidx9, ŠOneLoneCoder 2019, 2020, 2021, 2022 + +*/ + +#include "olcPixelGameEngine.h" + +namespace olc::utils::geom2d +{ + // Lemon Meringue + const double pi = 3.141592653589793238462643383279502884; + + // Floating point error margin + const double epsilon = 0.0001; + + //https://stackoverflow.com/questions/1903954/is-there-a-standard-sign-function-signum-sgn-in-c-c + template + constexpr int sgn(T val) { return (T(0) < val) - (val < T(0)); } + + // Defines a line segment + template + struct line + { + olc::v2d_generic start; + olc::v2d_generic end; + + inline line(const olc::v2d_generic& s = { T(0), T(0) }, + const olc::v2d_generic& e = { T(0), T(0) }) + : start(s), end(e) + { } + + + // Get length of line + inline constexpr T length() + { + return (end - start).mag(); + } + + // Get length of line^2 + inline constexpr T length2() + { + return (end - start).mag2(); + } + + inline constexpr olc::v2d_generic vector() const + { + return (end - start); + } + + // Given a real distance, get point along line + inline constexpr olc::v2d_generic rpoint(const T& distance) const + { + return start + (end - start).norm() * distance; + } + + // Given a unit distance, get point along line + inline constexpr olc::v2d_generic upoint(const T& distance) const + { + return start + (end - start) * distance; + } + + // Return which side of the line does a point lie + inline constexpr int32_t side(const olc::v2d_generic& point) const + { + double d = (end - start).cross(point - start); + if (d < 0) + return -1; + else + if (d > 0) + return 1; + else + return 0; + } + }; + + template + struct ray + { + olc::v2d_generic origin; + olc::v2d_generic direction; + }; + + template + struct rect + { + olc::v2d_generic pos; + olc::v2d_generic size; + + inline rect(const olc::v2d_generic& p = { T(0), T(0) }, + const olc::v2d_generic& s = { T(1), T(1) }) + : pos(p), size(s) + { } + + inline olc::v2d_generic middle() const + { + return pos + (size * double(0.5)); + } + + // Get line segment from top side of rectangle + inline line top() const + { + return { pos, {pos + size.x, pos.y } }; + } + + // Get line segment from bottom side of rectangle + inline line bottom() const + { + return { {pos.x, pos.y + size.y}, pos + size }; + } + + // Get line segment from left side of rectangle + inline line left() const + { + return { pos, {pos.x, pos.y + size.y} }; + } + + // Get line segment from right side of rectangle + inline line right() const + { + return { {pos + size.x, pos.y }, pos + size }; + } + + // Get a line from an indexed side, starting top, going clockwise + inline line side(const size_t i) const + { + if (i & 0b11 == 0) return top(); + if (i & 0b11 == 1) return right(); + if (i & 0b11 == 2) return bottom(); + if (i & 0b11 == 3) return left(); + } + + // Get area of rectangle + inline constexpr T area() const + { + return size.x * size.y; + } + + // Get perimeter of rectangle + inline constexpr T perimeter() const + { + return T(2) * (size.x + size.y); + } + }; + + + template + struct circle + { + olc::v2d_generic pos; + T radius = T(0); + + inline circle(const olc::v2d_generic& p = { T(0), T(0) }, const T r = T(0)) + : pos(p), radius(r) + { } + + // Get area of circle + inline constexpr T area() const + { + return T(pi) * radius * radius; + } + + // Get circumference of circle + inline constexpr T perimeter() const + { + return T(2.0 * pi) * radius; + } + + // Get circumference of circle + inline constexpr T circumference() const + { + return perimeter(); + } + }; + + + template + struct triangle + { + std::array, 3> pos; + + inline triangle(const olc::v2d_generic& p0 = { T(0), T(0) }, + const olc::v2d_generic& p1 = { T(0), T(0) }, + const olc::v2d_generic& p2 = { T(0), T(0) }) + : pos{ p0,p1,p2 } + { } + + // Get a line from an indexed side, starting top, going clockwise + inline line side(const size_t i) const + { + return line(pos[i % 3], pos[(i + 1) % 3]); + } + + // Get area of triangle + inline constexpr T area() const + { + return double(0.5) * std::abs( + (pos[0].x * (pos[1].y - pos[2].y)) + + (pos[1].x * (pos[2].y - pos[0].y)) + + (pos[2].x * (pos[0].y - pos[1].y))); + } + + // Get perimeter of triangle + inline constexpr T perimeter() const + { + return line(pos[0], pos[1]).length() + + line(pos[1], pos[2]).length() + + line(pos[2], pos[0]).length(); + } + }; + + + + // ========================================================================================================================= + // Closest(shape, point) =================================================================================================== + + // Returns closest point to point + template + inline olc::v2d_generic closest(const olc::v2d_generic& p1, const olc::v2d_generic& p2) + { + return p1; + } + + // Returns closest point on line to point + template + inline olc::v2d_generic closest(const line& l, const olc::v2d_generic& p) + { + // TODO: + return olc::v2d_generic(); + } + + // Returns closest point on circle to point + template + inline olc::v2d_generic closest(const circle& c, const olc::v2d_generic& p) + { + // TODO: + return olc::v2d_generic(); + } + + // Returns closest point on rectangle to point + template + inline olc::v2d_generic closest(const rect& r, const olc::v2d_generic& p) + { + // TODO: + return olc::v2d_generic(); + } + + // Returns closest point on triangle to point + template + inline olc::v2d_generic closest(const triangle& t, const olc::v2d_generic& p) + { + // TODO: + return olc::v2d_generic(); + } + + + + + + + + + + + + // ================================================================================================================ + // POINT ========================================================================================================== + + // Checks if point contains point + template + inline constexpr bool contains(const olc::v2d_generic& p1, const olc::v2d_generic& p2) + { + return (p1 - p2).mag2() < epsilon; + } + + // Checks if line contains point + template + inline constexpr bool contains(const line& l, const olc::v2d_generic& p) + { + double d = ((p.x - l.start.x) * (l.end.y - l.start.y) - (p.y - l.start.y) * (l.end.x - l.start.x)); + if (std::abs(d) < epsilon) + { + // point is on line + double u = l.vector().dot(p - l.start) / l.vector().mag2(); + return (u >= double(0.0) && u <= double(1.0)); + } + + return false; + } + + // Checks if rectangle contains point + template + inline constexpr bool contains(const rect& r, const olc::v2d_generic& p) + { + return !(p.x < r.pos.x || p.y < r.pos.y || + p.x >= (r.pos.x + r.size.x) || p.y >= (r.pos.y + r.size.y)); + } + + // Checks if circle contains a point + template + inline constexpr bool contains(const circle& c, const olc::v2d_generic& p) + { + return (c.pos - p).mag2() < (c.radius * c.radius); + } + + // Checks if triangle contains a point + template + inline constexpr bool contains(const triangle& c, const olc::v2d_generic& p) + { + // TODO: + return false; + } + + + + + // Check if point overlaps with point (analagous to contains()) + template + inline constexpr bool overlaps(const olc::v2d_generic& p1, const olc::v2d_generic& p2) + { + return contains(p1, p2); + } + + // Checks if line segment overlaps with point + template + inline constexpr bool overlaps(const line& l, const olc::v2d_generic& p) + { + return contains(l, p); + } + + // Checks if rectangle overlaps with point + template + inline constexpr bool overlaps(const rect& r, const olc::v2d_generic& p) + { + return contains(r, p); + } + + // Checks if circle overlaps with point + template + inline constexpr bool overlaps(const circle& c, const olc::v2d_generic& p) + { + return contains(c, p); + } + + // Checks if triangle overlaps with point + template + inline constexpr bool overlaps(const circle& c, const olc::v2d_generic& p) + { + return contains(t, p); + } + + + + + // Get intersection points where point intersects with point + template + inline std::vector> intersects(const olc::v2d_generic& p1, const olc::v2d_generic& p2) + { + if (contains(p1, p2)) + return { p1 }; + else + return {}; + } + + // Get intersection points where line segment intersects with point + template + inline std::vector> intersects(const line& l, const olc::v2d_generic& p) + { + if (contains(l, p)) + return { p }; + else + return {}; + } + + // Get intersection points where rectangle intersects with point + template + inline std::vector> intersects(const rect& r, const olc::v2d_generic& p) + { + std::vector> vPoints; + if (contains(r.top())) vPoints.push_back(p); + if (contains(r.bottom())) vPoints.push_back(p); + if (contains(r.left())) vPoints.push_back(p); + if (contains(r.right())) vPoints.push_back(p); + return vPoints; + } + + // Get intersection points where circle intersects with point + template + inline std::vector> intersects(const circle& c, const olc::v2d_generic& p) + { + if (std::abs((p - c.pos).mag2() - (c.radius * c.radius)) <= epsilon) + return { p }; + else + return {}; + } + + // Get intersection points where triangle intersects with point + template + inline std::vector> intersects(const triangle& r, const olc::v2d_generic& p) + { + // TODO: + return false; + } + + + + + + + + + + + + + // ================================================================================================================ + // LINE =========================================================================================================== + + // Check if point contains line segment + template + inline constexpr bool contains(const olc::v2d_generic& p, const line& l) + { + return false; // It can't! + } + + // Check if line segment contains line segment + template + inline constexpr bool contains(const line& l1, const line& l2) + { + // TODO: Check if segments are colinear, and l1 exists within bounds of l2 + return false; + } + + // Check if rectangle contains line segment + template + inline constexpr bool contains(const rect& r, const line& l) + { + return contains(r, l.start) && contains(r, l.end); + } + + // Check if circle contains line segment + template + inline constexpr bool contains(const circle& c1, const line& l) + { + // TODO: + return false; + } + + // Check if triangle contains line segment + template + inline constexpr bool contains(const triangle& t, const line& l) + { + // TODO: + return false; + } + + + + + // Check if point overlaps line segment + template + inline constexpr bool overlaps(const olc::v2d_generic& p, const line& l) + { + return contains(l, p); + } + + // Check if line segment overlaps line segment + template + inline constexpr bool overlaps(const line& l1, const line& l2) + { + // TODO: + return false; + } + + // Check if rectangle overlaps line segment + template + inline constexpr bool overlaps(const rect& r, const line& l) + { + return overlaps(r.top(), l) || overlaps(r.bottom(), l) + || overlaps(r.left(), l) || overlaps(r.right(), l); + } + + // Check if circle overlaps line segment + template + inline constexpr bool overlaps(const circle& c, const line& l) + { + // TODO: + return false; + } + + // Check if triangle overlaps line segment + template + inline constexpr bool overlaps(const triangle& t, const line& l) + { + // TODO: + return false; + } + + + + + // Get intersection points where point intersects with line segment + template + inline std::vector> intersects(const olc::v2d_generic& p, const line& l) + { + // TODO: + return false; + } + + // Get intersection points where line segment intersects with line segment + template + inline std::vector> intersects(const line& l1, const line& l2) + { + // TODO: + return false; + } + + // Get intersection points where rectangle intersects with line segment + template + inline std::vector> intersects(const rect& r, const line& l) + { + // TODO: + return false; + } + + // Get intersection points where circle intersects with line segment + template + inline std::vector> intersects(const circle& c, const line& l) + { + // TODO: + return false; + } + + // Get intersection points where triangle intersects with line segment + template + inline std::vector> intersects(const triangle& t, const line& l) + { + // TODO: + return false; + } + + + + + + + + + + + + + // ================================================================================================================ + // RECTANGLE ====================================================================================================== + + // Check if point contains rectangle + template + inline constexpr bool contains(const olc::v2d_generic& p, const rect& r) + { + return false; // It can't! + } + + // Check if line segment contains rectangle + template + inline constexpr bool contains(const line& l, const rect& r) + { + return false; // It can't + } + + // Check if rectangle contains rectangle + template + inline constexpr bool contains(const rect& r1, const rect& r2) + { + return (r2.pos.x >= r1.pos.x) && (r2.pos.x + r2.size.x < r1.pos.x + r1.size.x) && + (r2.pos.y >= r1.pos.y) && (r2.pos.y + r2.size.y < r1.pos.y + r1.size.y); + } + + // Check if circle contains rectangle + template + inline constexpr bool contains(const circle& c, const rect& r) + { + // TODO: + return false; + } + + // Check if triangle contains rectangle + template + inline constexpr bool contains(const triangle& t, const rect& r) + { + // TODO: + return false; + } + + + + + // Check if point overlaps rectangle + template + inline constexpr bool overlaps(const olc::v2d_generic& p, const rect& r) + { + return overlaps(r, p); + } + + // Check if line segment overlaps rectangle + template + inline constexpr bool overlaps(const line& l, const rect& r) + { + return overlaps(r, l); + } + + // Check if rectangle overlaps rectangle + template + inline constexpr bool overlaps(const rect& l, const rect& r) + { + return (r1.pos.x < r2.pos.x + r2.size.x && r1.pos.x + r1.size.x >= r2.pos.x && + r1.pos.y < r2.pos.y + r2.size.y && r1.pos.y + r1.size.y >= r2.pos.y); + } + + // Check if circle overlaps rectangle + template + inline constexpr bool overlaps(const circle& c, const rect& r) + { + // TODO: + return false; + } + + // Check if triangle overlaps rectangle + template + inline constexpr bool overlaps(const triangle& t, const rect& r) + { + // TODO: + return false; + } + + + + + // Get intersection points where point intersects with rectangle + template + inline std::vector> intersects(const olc::v2d_generic& p, const rect& r) + { + // TODO: + return false; + } + + // Get intersection points where line segment intersects with rectangle + template + inline std::vector> intersects(const line& l, const rect& r) + { + // TODO: + return false; + } + + // Get intersection points where rectangle intersects with rectangle + template + inline std::vector> intersects(const rect& r1, const rect& r2) + { + // TODO: + return false; + } + + // Get intersection points where circle intersects with rectangle + template + inline std::vector> intersects(const circle& c, const rect& r) + { + // TODO: + return false; + } + + // Get intersection points where triangle intersects with rectangle + template + inline std::vector> intersects(const triangle& t, const rect& r) + { + // TODO: + return false; + } + + + + + + + + + + + + + + // ================================================================================================================ + // CIRCLE ========================================================================================================= + + // Check if point contains circle + template + inline constexpr bool contains(const olc::v2d_generic& p, const circle& c) + { + return false; // It can't! + } + + // Check if line segment contains circle + template + inline constexpr bool contains(const line& l, const circle& c) + { + return false; // It can't! + } + + // Check if rectangle contains circle + template + inline constexpr bool contains(const rect& r, const circle& c) + { + // TODO: + return false; + } + + // Check if circle contains circle + template + inline constexpr bool contains(const circle& c1, const circle& c2) + { + return (c1.pos - c2.pos).mag2() <= (c1.radius * c1.radius) - (c2.radius * c2.radius); + } + + // Check if triangle contains circle + template + inline constexpr bool contains(const triangle& t, const circle& c) + { + // TODO: + return false; + } + + + + + // Check if point overlaps circle + template + inline constexpr bool overlaps(const olc::v2d_generic& p, const circle& c) + { + return overlaps(c, p); + } + + // Check if line segment overlaps circle + template + inline constexpr bool overlaps(const line& l, const circle& c) + { + return overlaps(c, l); + } + + // Check if rectangle overlaps circle + template + inline constexpr bool overlaps(const rect& l, const circle& c) + { + return overlaps(c, r); + } + + // Check if circle overlaps circle + template + inline constexpr bool overlaps(const circle& c1, const circle& c2) + { + return (c1.pos - c2.pos).mag2() <= (c1.radius * c1.radius) + (c2.radius * c2.radius); + } + + // Check if triangle overlaps circle + template + inline constexpr bool overlaps(const triangle& t, const circle& c) + { + // TODO: + return false; + } + + + + + // Get intersection points where point intersects with circle + template + inline std::vector> intersects(const olc::v2d_generic& p, const circle& c) + { + // TODO: + return false; + } + + // Get intersection points where line segment intersects with circle + template + inline std::vector> intersects(const line& l, const circle& c) + { + // TODO: + return false; + } + + // Get intersection points where rectangle intersects with circle + template + inline std::vector> intersects(const rect& r, const circle& c) + { + // TODO: + return false; + } + + // Get intersection points where circle intersects with circle + template + inline std::vector> intersects(const circle& c1, const circle& c2) + { + // TODO: + return false; + } + + // Get intersection points where triangle intersects with circle + template + inline std::vector> intersects(const triangle& t, const circle& c) + { + // TODO: + return false; + } + + + + + + + + + + + + + + // ================================================================================================================ + // TRIANGLE ======================================================================================================= + + // Check if point contains triangle + template + inline constexpr bool contains(const olc::v2d_generic& p, const triangle& t) + { + return false; // It can't! + } + + // Check if line segment contains triangle + template + inline constexpr bool contains(const line& l, const triangle& t) + { + return false; // It can't + } + + // Check if rectangle contains triangle + template + inline constexpr bool contains(const rect& r, const triangle& t) + { + // TODO: + return false; + } + + // Check if circle contains triangle + template + inline constexpr bool contains(const circle& c, const triangle& t) + { + // TODO: + return false; + } + + // Check if triangle contains triangle + template + inline constexpr bool contains(const triangle& t1, const triangle& t2) + { + // TODO: + return false; + } + + + + + // Check if point overlaps triangle + template + inline constexpr bool overlaps(const olc::v2d_generic& p, const triangle& t) + { + return overlaps(t, p); + } + + // Check if line segment overlaps triangle + template + inline constexpr bool overlaps(const line& l, const triangle& t) + { + return overlaps(t, l); + } + + // Check if rectangle overlaps triangle + template + inline constexpr bool overlaps(const rect& r, const triangle& t) + { + return overlaps(t, r); + } + + // Check if circle overlaps triangle + template + inline constexpr bool overlaps(const circle& c, const triangle& t) + { + return overlaps(t, c); + } + + // Check if triangle overlaps triangle + template + inline constexpr bool overlaps(const triangle& t1, const triangle& t2) + { + // TODO: + return false; + } + + + + + // Get intersection points where point intersects with triangle + template + inline std::vector> intersects(const olc::v2d_generic& p, const triangle& t) + { + // TODO: + return false; + } + + // Get intersection points where line segment intersects with triangle + template + inline std::vector> intersects(const line& l, const triangle& t) + { + // TODO: + return false; + } + + // Get intersection points where rectangle intersects with triangle + template + inline std::vector> intersects(const rect& r, const triangle& t) + { + // TODO: + return false; + } + + // Get intersection points where circle intersects with triangle + template + inline std::vector> intersects(const circle& c, const triangle& t) + { + // TODO: + return false; + } + + // Get intersection points where triangle intersects with triangle + template + inline std::vector> intersects(const triangle& t1, const triangle& t2) + { + // TODO: + return false; + } + +} \ No newline at end of file From 025dcbdc4e28c7db55092eb62886e4878c8c4044 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Mon, 19 Sep 2022 16:08:06 +0100 Subject: [PATCH 02/10] Some minor compile bug fixes --- utilities/olcUTIL_Geometry2D.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utilities/olcUTIL_Geometry2D.h b/utilities/olcUTIL_Geometry2D.h index 21f50c1..205820f 100644 --- a/utilities/olcUTIL_Geometry2D.h +++ b/utilities/olcUTIL_Geometry2D.h @@ -393,7 +393,7 @@ namespace olc::utils::geom2d // Checks if triangle overlaps with point template - inline constexpr bool overlaps(const circle& c, const olc::v2d_generic& p) + inline constexpr bool overlaps(const triangle& t, const olc::v2d_generic& p) { return contains(t, p); } @@ -659,7 +659,7 @@ namespace olc::utils::geom2d // Check if rectangle overlaps rectangle template - inline constexpr bool overlaps(const rect& l, const rect& r) + inline constexpr bool overlaps(const rect& r1, const rect& r2) { return (r1.pos.x < r2.pos.x + r2.size.x && r1.pos.x + r1.size.x >= r2.pos.x && r1.pos.y < r2.pos.y + r2.size.y && r1.pos.y + r1.size.y >= r2.pos.y); @@ -795,7 +795,7 @@ namespace olc::utils::geom2d // Check if rectangle overlaps circle template - inline constexpr bool overlaps(const rect& l, const circle& c) + inline constexpr bool overlaps(const rect& r, const circle& c) { return overlaps(c, r); } From e7c20b096d3ba83120bec5bd6bdc028786d66b3f Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Mon, 19 Sep 2022 16:21:24 +0100 Subject: [PATCH 03/10] Fixed more template bugs - made rectangle overlap fair on all sides --- utilities/olcUTIL_Geometry2D.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/utilities/olcUTIL_Geometry2D.h b/utilities/olcUTIL_Geometry2D.h index 205820f..b1475fc 100644 --- a/utilities/olcUTIL_Geometry2D.h +++ b/utilities/olcUTIL_Geometry2D.h @@ -149,7 +149,7 @@ namespace olc::utils::geom2d // Get line segment from top side of rectangle inline line top() const { - return { pos, {pos + size.x, pos.y } }; + return { pos, {pos.x + size.x, pos.y } }; } // Get line segment from bottom side of rectangle @@ -167,7 +167,7 @@ namespace olc::utils::geom2d // Get line segment from right side of rectangle inline line right() const { - return { {pos + size.x, pos.y }, pos + size }; + return { {pos.x + size.x, pos.y }, pos + size }; } // Get a line from an indexed side, starting top, going clockwise @@ -342,7 +342,7 @@ namespace olc::utils::geom2d inline constexpr bool contains(const rect& r, const olc::v2d_generic& p) { return !(p.x < r.pos.x || p.y < r.pos.y || - p.x >= (r.pos.x + r.size.x) || p.y >= (r.pos.y + r.size.y)); + p.x > (r.pos.x + r.size.x) || p.y > (r.pos.y + r.size.y)); } // Checks if circle contains a point @@ -426,10 +426,10 @@ namespace olc::utils::geom2d inline std::vector> intersects(const rect& r, const olc::v2d_generic& p) { std::vector> vPoints; - if (contains(r.top())) vPoints.push_back(p); - if (contains(r.bottom())) vPoints.push_back(p); - if (contains(r.left())) vPoints.push_back(p); - if (contains(r.right())) vPoints.push_back(p); + if (contains(r.top(), p)) vPoints.push_back(p); + if (contains(r.bottom(), p)) vPoints.push_back(p); + if (contains(r.left(), p)) vPoints.push_back(p); + if (contains(r.right(), p)) vPoints.push_back(p); return vPoints; } @@ -448,7 +448,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const triangle& r, const olc::v2d_generic& p) { // TODO: - return false; + return {}; } From a88cd7afb501e0c1f119cb654319d39fd7137659 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Mon, 19 Sep 2022 20:47:38 +0100 Subject: [PATCH 04/10] Fixed yet more template bugs, started to add triangle tests --- utilities/olcUTIL_Geometry2D.h | 76 +++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 34 deletions(-) diff --git a/utilities/olcUTIL_Geometry2D.h b/utilities/olcUTIL_Geometry2D.h index b1475fc..3bae5f6 100644 --- a/utilities/olcUTIL_Geometry2D.h +++ b/utilities/olcUTIL_Geometry2D.h @@ -354,10 +354,14 @@ namespace olc::utils::geom2d // Checks if triangle contains a point template - inline constexpr bool contains(const triangle& c, const olc::v2d_generic& p) + inline constexpr bool contains(const triangle& t, const olc::v2d_generic& p) { - // TODO: - return false; + // http://jsfiddle.net/PerroAZUL/zdaY8/1/ + T2 A = T2(0.5) * (-t.pos[1].y * t.pos[2].x + t.pos[0].y * (-t.pos[1].x + t.pos[2].x) + t.pos[0].x * (t.pos[1].y - t.pos[2].y) + t.pos[1].x * t.pos[2].y); + T2 sign = A < T2(0) ? T2(-1) : T2(1); + T2 s = (t.pos[0].y * t.pos[2].x - t.pos[0].x * t.pos[2].y + (t.pos[2].y - t.pos[0].y) * p.x + (t.pos[0].x - t.pos[2].x) * p.y) * sign; + T2 v = (t.pos[0].x * t.pos[1].y - t.pos[0].y * t.pos[1].x + (t.pos[0].y - t.pos[1].y) * p.x + (t.pos[1].x - t.pos[0].x) * p.y) * sign; + return s > T2(0) && v > T2(0) && (s + v) < T2(2) * A * sign; } @@ -499,8 +503,7 @@ namespace olc::utils::geom2d template inline constexpr bool contains(const triangle& t, const line& l) { - // TODO: - return false; + return contains(t, l.start) && contains(t, l.end); } @@ -525,8 +528,8 @@ namespace olc::utils::geom2d template inline constexpr bool overlaps(const rect& r, const line& l) { - return overlaps(r.top(), l) || overlaps(r.bottom(), l) - || overlaps(r.left(), l) || overlaps(r.right(), l); + return contains(r, l.start) + || contains(r, l.end); } // Check if circle overlaps line segment @@ -541,8 +544,7 @@ namespace olc::utils::geom2d template inline constexpr bool overlaps(const triangle& t, const line& l) { - // TODO: - return false; + return overlaps(t, l.start) || overlaps(t, l.end); } @@ -553,7 +555,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const olc::v2d_generic& p, const line& l) { // TODO: - return false; + return {}; } // Get intersection points where line segment intersects with line segment @@ -561,7 +563,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const line& l1, const line& l2) { // TODO: - return false; + return {}; } // Get intersection points where rectangle intersects with line segment @@ -569,7 +571,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const rect& r, const line& l) { // TODO: - return false; + return {}; } // Get intersection points where circle intersects with line segment @@ -577,7 +579,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const circle& c, const line& l) { // TODO: - return false; + return {}; } // Get intersection points where triangle intersects with line segment @@ -585,7 +587,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const triangle& t, const line& l) { // TODO: - return false; + return {}; } @@ -636,8 +638,10 @@ namespace olc::utils::geom2d template inline constexpr bool contains(const triangle& t, const rect& r) { - // TODO: - return false; + return contains(t, r.pos) + && contains(t, r.pos + r.size) + && contains(t, olc::v2d_generic{ r.pos.x + r.size.x,r.pos.y }) + && contains(t, olc::v2d_generic{ r.pos.x, r.pos.y + r.size.y }); } @@ -677,8 +681,13 @@ namespace olc::utils::geom2d template inline constexpr bool overlaps(const triangle& t, const rect& r) { - // TODO: - return false; + return contains(t, r.pos) + || contains(t, r.pos + r.size) + || contains(t, olc::v2d_generic{ r.pos.x + r.size.x, r.pos.y }) + || contains(t, olc::v2d_generic{ r.pos.x, r.pos.y + r.size.y }); + + // TODO: This method is no good, consider rectangle with all vertices + // outside of triangle, but edges still crossing } @@ -688,8 +697,7 @@ namespace olc::utils::geom2d template inline std::vector> intersects(const olc::v2d_generic& p, const rect& r) { - // TODO: - return false; + return intersects(r, p); } // Get intersection points where line segment intersects with rectangle @@ -697,7 +705,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const line& l, const rect& r) { // TODO: - return false; + return {}; } // Get intersection points where rectangle intersects with rectangle @@ -705,7 +713,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const rect& r1, const rect& r2) { // TODO: - return false; + return {}; } // Get intersection points where circle intersects with rectangle @@ -713,7 +721,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const circle& c, const rect& r) { // TODO: - return false; + return {}; } // Get intersection points where triangle intersects with rectangle @@ -721,7 +729,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const triangle& t, const rect& r) { // TODO: - return false; + return {}; } @@ -823,7 +831,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const olc::v2d_generic& p, const circle& c) { // TODO: - return false; + return {}; } // Get intersection points where line segment intersects with circle @@ -831,7 +839,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const line& l, const circle& c) { // TODO: - return false; + return {}; } // Get intersection points where rectangle intersects with circle @@ -839,7 +847,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const rect& r, const circle& c) { // TODO: - return false; + return {}; } // Get intersection points where circle intersects with circle @@ -847,7 +855,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const circle& c1, const circle& c2) { // TODO: - return false; + return {}; } // Get intersection points where triangle intersects with circle @@ -855,7 +863,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const triangle& t, const circle& c) { // TODO: - return false; + return {}; } @@ -958,7 +966,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const olc::v2d_generic& p, const triangle& t) { // TODO: - return false; + return {}; } // Get intersection points where line segment intersects with triangle @@ -966,7 +974,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const line& l, const triangle& t) { // TODO: - return false; + return {}; } // Get intersection points where rectangle intersects with triangle @@ -974,7 +982,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const rect& r, const triangle& t) { // TODO: - return false; + return {}; } // Get intersection points where circle intersects with triangle @@ -982,7 +990,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const circle& c, const triangle& t) { // TODO: - return false; + return {}; } // Get intersection points where triangle intersects with triangle @@ -990,7 +998,7 @@ namespace olc::utils::geom2d inline std::vector> intersects(const triangle& t1, const triangle& t2) { // TODO: - return false; + return {}; } } \ No newline at end of file From 6a1105fd3e1fbe7bd170cb0bf4da3b7ee69c39c2 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Mon, 19 Sep 2022 21:44:30 +0100 Subject: [PATCH 05/10] Fixed Circle V Circle --- utilities/olcUTIL_Geometry2D.h | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/utilities/olcUTIL_Geometry2D.h b/utilities/olcUTIL_Geometry2D.h index 3bae5f6..0400598 100644 --- a/utilities/olcUTIL_Geometry2D.h +++ b/utilities/olcUTIL_Geometry2D.h @@ -61,7 +61,7 @@ namespace olc::utils::geom2d const double pi = 3.141592653589793238462643383279502884; // Floating point error margin - const double epsilon = 0.0001; + const double epsilon = 0.001; //https://stackoverflow.com/questions/1903954/is-there-a-standard-sign-function-signum-sgn-in-c-c template @@ -228,7 +228,8 @@ namespace olc::utils::geom2d { std::array, 3> pos; - inline triangle(const olc::v2d_generic& p0 = { T(0), T(0) }, + inline triangle( + const olc::v2d_generic& p0 = { T(0), T(0) }, const olc::v2d_generic& p1 = { T(0), T(0) }, const olc::v2d_generic& p2 = { T(0), T(0) }) : pos{ p0,p1,p2 } @@ -530,6 +531,9 @@ namespace olc::utils::geom2d { return contains(r, l.start) || contains(r, l.end); + + // TODO: This method is no good, it cant detect lines whose start and end + // points are outside the rectangle } // Check if circle overlaps line segment @@ -545,6 +549,9 @@ namespace olc::utils::geom2d inline constexpr bool overlaps(const triangle& t, const line& l) { return overlaps(t, l.start) || overlaps(t, l.end); + + // TODO: This method is no good, it cant detect lines whose start and end + // points are outside the triangle } @@ -773,7 +780,7 @@ namespace olc::utils::geom2d template inline constexpr bool contains(const circle& c1, const circle& c2) { - return (c1.pos - c2.pos).mag2() <= (c1.radius * c1.radius) - (c2.radius * c2.radius); + return (c1.pos - c2.pos).mag2() <= (c1.radius - c2.radius) * (c1.radius - c2.radius); } // Check if triangle contains circle @@ -812,7 +819,7 @@ namespace olc::utils::geom2d template inline constexpr bool overlaps(const circle& c1, const circle& c2) { - return (c1.pos - c2.pos).mag2() <= (c1.radius * c1.radius) + (c2.radius * c2.radius); + return (c1.pos - c2.pos).mag2() <= (c1.radius + c2.radius) * (c1.radius + c2.radius); } // Check if triangle overlaps circle From 12c8638c152e00e47a3708602aba99c12d69792d Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Tue, 20 Sep 2022 21:35:46 +0100 Subject: [PATCH 06/10] Added Utility Palette, and Utility Quadtree --- extensions/olcPGEX_QuickGUI.h | 2 +- utilities/coming_soon.txt | 0 utilities/olcUTIL_Palette.h | 238 +++++++++++++++++++++++ utilities/olcUTIL_QuadTree.h | 353 ++++++++++++++++++++++++++++++++++ 4 files changed, 592 insertions(+), 1 deletion(-) delete mode 100644 utilities/coming_soon.txt create mode 100644 utilities/olcUTIL_Palette.h create mode 100644 utilities/olcUTIL_QuadTree.h diff --git a/extensions/olcPGEX_QuickGUI.h b/extensions/olcPGEX_QuickGUI.h index 664d0c2..cbf5647 100644 --- a/extensions/olcPGEX_QuickGUI.h +++ b/extensions/olcPGEX_QuickGUI.h @@ -649,7 +649,7 @@ namespace olc::QuickGUI Button::Draw(pge); if (bChecked) - pge->DrawRect(vPos + olc::vf2d(2, 2), vSize - olc::vi2d(4, 4), m_manager.colBorder); + pge->DrawRect(vPos + olc::vf2d(2, 2), vSize - olc::vi2d(5, 5), m_manager.colBorder); } void CheckBox::DrawDecal(olc::PixelGameEngine* pge) diff --git a/utilities/coming_soon.txt b/utilities/coming_soon.txt deleted file mode 100644 index e69de29..0000000 diff --git a/utilities/olcUTIL_Palette.h b/utilities/olcUTIL_Palette.h new file mode 100644 index 0000000..68b1755 --- /dev/null +++ b/utilities/olcUTIL_Palette.h @@ -0,0 +1,238 @@ +/* + OneLoneCoder - Palette v1.00 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Convenient tool for creating interpolated palettes, that can be + sampled discretely or continuously + + + License (OLC-3) + ~~~~~~~~~~~~~~~ + + Copyright 2018 - 2022 OneLoneCoder.com + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions or derivations of source code must retain the above + copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions or derivative works in binary form must reproduce + the above copyright notice. This list of conditions and the following + disclaimer must be reproduced in the documentation and/or other + materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Links + ~~~~~ + YouTube: https://www.youtube.com/javidx9 + Discord: https://discord.gg/WhwHUMV + Twitter: https://www.twitter.com/javidx9 + Twitch: https://www.twitch.tv/javidx9 + GitHub: https://www.github.com/onelonecoder + Homepage: https://www.onelonecoder.com + + Author + ~~~~~~ + David Barr, aka javidx9, ŠOneLoneCoder 2019, 2020, 2021, 2022 + +*/ + +/* + Example + ~~~~~~~ + + // Create palette object + olc::Palette pal; + + // Use a stock palette + pal = Palette(Stock::Spectrum); + + // Clearing a palete + pal.Clear(); + + // Custom palette - normalised index [0.0 ... 1.0], Colour + pal.SetColour(00.0 / 24.0, olc::Pixel(000, 000, 000)); + pal.SetColour(03.0 / 24.0, olc::Pixel(000, 000, 030)); + pal.SetColour(05.0 / 24.0, olc::Pixel(200, 240, 255)); + pal.SetColour(12.0 / 24.0, olc::Pixel(200, 240, 255)); + pal.SetColour(15.0 / 24.0, olc::Pixel(050, 100, 255)); + pal.SetColour(20.0 / 24.0, olc::Pixel(245, 000, 146)); + pal.SetColour(22.0 / 24.0, olc::Pixel(000, 000, 128)); + pal.SetColour(24.0 / 24.0, olc::Pixel(000, 000, 000)); + + // Access palette via interpolation [0.0 ... 1.0] + // Continuous, Smooth, potentially slower + olc::Pixel p1 = pal.Sample(0.2); + olc::Pixel p2 = pal.Sample(0.6); + olc::Pixel p3 = pal.Sample(1.0); + + // Access palette via index [0 ... 255] + // Discrete, Not Smooth, very fast + olc::Pixel i1 = pal.Index(10); +*/ + + + +#pragma once + +#include "olcPixelGameEngine.h" + +namespace olc::utils +{ + class Palette + { + public: + enum class Stock + { + Empty, + Greyscale, + ColdHot, + Spectrum, + }; + + public: + // Construct empty palette (default) or populate with pre-defined + inline Palette(const Stock stock = Stock::Empty) + { + switch (stock) + { + case Stock::Empty: + Clear(); + break; + case Stock::Greyscale: + vColours = + { + {0.0, olc::BLACK}, {1.0, olc::WHITE} + }; + break; + case Stock::ColdHot: + vColours = + { + {0.0, olc::CYAN}, {0.5, olc::BLACK}, {1.0, olc::YELLOW} + }; + break; + case Stock::Spectrum: + vColours = + { + {0.0 / 6.0, olc::RED}, + {1.0 / 6.0, olc::YELLOW}, + {2.0 / 6.0, olc::GREEN}, + {3.0 / 6.0, olc::CYAN}, + {4.0 / 6.0, olc::BLUE}, + {5.0 / 6.0, olc::MAGENTA}, + {6.0 / 6.0, olc::RED} + }; + break; + } + + ReconstructIndex(); + } + + public: + // Sample continously from palette, where t is [0.0 ... 1.0] + inline olc::Pixel Sample(const double t) const + { + // Return obvious sample values + if (vColours.empty()) + return olc::BLACK; + + if (vColours.size() == 1) + return vColours.front().second; + + // Iterate through colour entries until we find the first entry + // with a location greater than our sample point + double i = std::clamp(t, 0.0, 1.0); + auto it = vColours.begin(); + while (i > it->first) + ++it; + + // If that is the first entry, just return it + if (it == std::begin(vColours)) + return it->second; + else + { + // else get the preceeding entry, and lerp between the two + // proportionally + auto it_p = std::prev(it); + return olc::PixelLerp(it_p->second, it->second, + float((i - it_p->first) / (it->first - it_p->first))); + } + } + + // Sample discretely from palette where idx is [0 ... 255] + inline olc::Pixel Index(const uint8_t idx) const + { + return aIndexedPalette[idx]; + } + + // Clear palette, defaulting to black + inline void Clear() + { + vColours.clear(); + aIndexedPalette.fill(olc::BLACK); + } + + // Add a colour at a particular continuous location in palette + inline void SetColour(const double d, const olc::Pixel col) + { + double i = std::clamp(d, 0.0, 1.0); + + // If d already exists, replace it + auto it = std::find_if(vColours.begin(), vColours.end(), + [&i](const std::pair& p) + { + return p.first == i; + }); + + if (it != std::end(vColours)) + { + // Palette entry was found, so replace colour entry + it->second = col; + } + else + { + // Palette entry not found, sp add it, and sort palette vector + vColours.push_back({ i, col }); + std::sort(vColours.begin(), vColours.end(), + [](const std::pair& p1, std::pair& p2) + { + return p2.first > p1.first; + }); + } + + ReconstructIndex(); + } + + private: + // Vector stores all palette milestones + std::vector> vColours; + // Array stores fast LUT + std::array aIndexedPalette; + + inline void ReconstructIndex() + { + // Reconstruct Indexed palette + for (int i = 0; i < 256; i++) + { + double t = double(i) / 255.0; + aIndexedPalette[i] = Sample(t); + } + } + }; +} \ No newline at end of file diff --git a/utilities/olcUTIL_QuadTree.h b/utilities/olcUTIL_QuadTree.h new file mode 100644 index 0000000..3dbccbb --- /dev/null +++ b/utilities/olcUTIL_QuadTree.h @@ -0,0 +1,353 @@ +/* + OneLoneCoder - QuadTree v1.00 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + A Dynamic geom2d::rect-Tree implementation to store objects in a 2D space + with a fast retrieval. + + License (OLC-3) + ~~~~~~~~~~~~~~~ + + Copyright 2018 - 2022 OneLoneCoder.com + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions or derivations of source code must retain the above + copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions or derivative works in binary form must reproduce + the above copyright notice. This list of conditions and the following + disclaimer must be reproduced in the documentation and/or other + materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Links + ~~~~~ + YouTube: https://www.youtube.com/javidx9 + Discord: https://discord.gg/WhwHUMV + Twitter: https://www.twitter.com/javidx9 + Twitch: https://www.twitch.tv/javidx9 + GitHub: https://www.github.com/onelonecoder + Homepage: https://www.onelonecoder.com + + Author + ~~~~~~ + David Barr, aka javidx9, ŠOneLoneCoder 2019, 2020, 2021, 2022 + +*/ + + +#pragma once + +#include "olcPixelGameEngine.h" +#include "olcUTIL_Geometry2D.h" + +constexpr size_t MAX_DEPTH = 8; + +namespace olc::utils +{ + //struct geom2d::rect + //{ + // olc::vf2d pos; + // olc::vf2d size; + + // geom2d::rect(const olc::vf2d& p = { 0.0f, 0.0f }, const olc::vf2d& s = { 1.0f, 1.0f }) : pos(p), size(s) + // { + + // } + + // constexpr bool contains(const olc::vf2d& p) const + // { + // return !(p.x < pos.x || p.y < pos.y || p.x >= (pos.x + size.x) || p.y >= (pos.y + size.y)); + // } + + // constexpr bool contains(const geom2d::rect& r) const + // { + // return (r.pos.x >= pos.x) && (r.pos.x + r.size.x < pos.x + size.x) && + // (r.pos.y >= pos.y) && (r.pos.y + r.size.y < pos.y + size.y); + // } + + // constexpr bool overlaps(const geom2d::rect& r) const + // { + // return (pos.x < r.pos.x + r.size.x && pos.x + size.x >= r.pos.x && pos.y < r.pos.y + r.size.y && pos.y + size.y >= r.pos.y); + // } + //}; + + template + struct QuadTreeItemLocation + { + typename std::list, T>>* container = nullptr; + typename std::list, T>>::iterator iterator; + }; + + template + class DynamicQuadTree + { + public: + DynamicQuadTree(const geom2d::rect& size, const size_t nDepth = 0) + { + m_depth = nDepth; + m_rect = size; + resize(m_rect); + } + + // Insert a region into this area + QuadTreeItemLocation insert(const pT item, const geom2d::rect& itemsize) + { + for (int i = 0; i < 4; i++) + { + if (geom2d::contains(m_rChild[i], itemsize)) + { + // Have we reached depth limit? + if (m_depth + 1 < MAX_DEPTH) + { + // No, so does child exist? + if (!m_pChild[i]) + { + // No, so create it + m_pChild[i] = std::make_shared>(m_rChild[i], m_depth + 1); + } + + // Yes, so add item to it + return m_pChild[i]->insert(item, itemsize); + } + } + } + + // It didnt fit, so item must belong to this geom2d::rect + m_pItems.push_back({ itemsize, item }); + return { &m_pItems, std::prev(m_pItems.end()) }; + } + + void relocate(pT item, const geom2d::rect& rArea) + { + // Remove it + remove(item); + + // Reinsert it with new location + insert(item, rArea); + } + + size_t size() const + { + size_t nCount = m_pItems.size(); + for (int i = 0; i < 4; i++) + if (m_pChild[i]) nCount += m_pChild[i]->size(); + return nCount; + } + + void search(const geom2d::rect& rArea, std::list& listItems) const + { + // First, check for items belonging to this area, add them to the list + // if there is overlap + for (const auto& p : m_pItems) + { + if (geom2d::overlaps(rArea,p.first)) + listItems.push_back(p.second); + } + + // Second, recurse through children and see if they can + // add to the list + for (int i = 0; i < 4; i++) + { + if (m_pChild[i]) + { + // If child is entirely contained within area, recursively + // add all of its children, no need to check boundaries + if (geom2d::contains(rArea,m_rChild[i])) + m_pChild[i]->items(listItems); + + // If child overlaps with search area then checks need + // to be made + else if (geom2d::overlaps(m_rChild[i],rArea)) + m_pChild[i]->search(rArea, listItems); + } + } + + + } + + void items(std::list& listItems) const + { + // No questions asked, just return child items + for (const auto& p : m_pItems) + listItems.push_back(p.second); + + for (int i = 0; i < 4; i++) + if (m_pChild[i]) m_pChild[i]->items(listItems); + } + + void clear() + { + m_pItems.clear(); + + for (int i = 0; i < 4; i++) + { + if (m_pChild[i]) + m_pChild[i]->clear(); + else + m_pChild[i].reset(); + } + } + + void resize(const geom2d::rect& rArea) + { + clear(); + m_rect = rArea; + olc::vf2d vChildSize = m_rect.size / 2.0f; + m_rChild = + { + geom2d::rect(m_rect.pos, vChildSize), + geom2d::rect({m_rect.pos.x + vChildSize.x, m_rect.pos.y}, vChildSize), + geom2d::rect({m_rect.pos.x, m_rect.pos.y + vChildSize.y}, vChildSize), + geom2d::rect(m_rect.pos + vChildSize, vChildSize) + }; + } + + + const geom2d::rect& area() + { + return m_rect; + } + + protected: + size_t m_depth = 0; + + // Area of this quadnode + geom2d::rect m_rect; + + // 4 child areas of this quadnode + std::array, 4> m_rChild{}; + + // 4 potential children of this quadnode + std::array>, 4> m_pChild{}; + + // Items which belong to this quadnode + std::list, pT>> m_pItems; + + }; + + template + struct QuadTreeItem + { + // The item Itself + T item; + + // A "locator" to the container/iterator that points to this item's iterator in the + // top level list - phew + QuadTreeItemLocation>::iterator> pItem; + }; + + template + class QuadTreeContainer + { + using IQuadtreeContainer = std::list>; + + public: + QuadTreeContainer(const geom2d::rect& size = { {0.0f, 0.0f}, { 100.0f, 100.0f } }, const size_t nDepth = 0) : root(size, nDepth) + { + + } + + // Sets the spatial coverage area of teh quadtree + void resize(const geom2d::rect& rArea) + { + root.resize(rArea); + } + + // Inserts an item into the quadtree + void insert(const T& item, const geom2d::rect& itemsize) + { + QuadTreeItem newItem; + newItem.item = item; + + // Item i stored in container + m_allItems.emplace_back(newItem); + + // Pointer/Area of item is stored in geom2d::rect tree + m_allItems.back().pItem = root.insert(std::prev(m_allItems.end()), itemsize); + } + + // Returns a std::list of pointers to items within the search area + std::list search(const geom2d::rect& rArea) const + { + std::list listItemPointers; + root.search(rArea, listItemPointers); + return listItemPointers; + } + + void remove(typename IQuadtreeContainer::iterator& item) + { + // Iterator points to a QuadTreeItem + item->pItem.container->erase(item->pItem.iterator); + + // Remove actual item from container + m_allItems.erase(item); + } + + void relocate(typename IQuadtreeContainer::iterator& item, const geom2d::rect& itemsize) + { + // Remove pointer to item from whichever container its stored in + item->pItem.container->erase(item->pItem.iterator); + + // Update the items pointer by reinsertion into geom2d::rect tree + item->pItem = root.insert(item, itemsize); + + } + + typename IQuadtreeContainer::iterator begin() + { + return m_allItems.begin(); + } + + typename IQuadtreeContainer::iterator end() + { + return m_allItems.end(); + } + + typename IQuadtreeContainer::const_iterator cbegin() + { + return m_allItems.cbegin(); + } + + typename IQuadtreeContainer::const_iterator cend() + { + return m_allItems.cend(); + } + + size_t size() const + { + return root.size(); + } + + void clear() + { + root.clear(); + } + + const geom2d::rect& area() + { + return root.area(); + } + + protected: + DynamicQuadTree root; + IQuadtreeContainer m_allItems; + }; +} From 854060277abd5d2c8e1e37b67d9e65cceea8d6c9 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Tue, 20 Sep 2022 21:43:33 +0100 Subject: [PATCH 07/10] Tidied up quad tree --- utilities/olcUTIL_QuadTree.h | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/utilities/olcUTIL_QuadTree.h b/utilities/olcUTIL_QuadTree.h index 3dbccbb..09ca105 100644 --- a/utilities/olcUTIL_QuadTree.h +++ b/utilities/olcUTIL_QuadTree.h @@ -1,7 +1,7 @@ /* OneLoneCoder - QuadTree v1.00 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - A Dynamic geom2d::rect-Tree implementation to store objects in a 2D space + A Dynamic Quad-Tree implementation to store objects in a 2D space with a fast retrieval. License (OLC-3) @@ -62,33 +62,6 @@ constexpr size_t MAX_DEPTH = 8; namespace olc::utils { - //struct geom2d::rect - //{ - // olc::vf2d pos; - // olc::vf2d size; - - // geom2d::rect(const olc::vf2d& p = { 0.0f, 0.0f }, const olc::vf2d& s = { 1.0f, 1.0f }) : pos(p), size(s) - // { - - // } - - // constexpr bool contains(const olc::vf2d& p) const - // { - // return !(p.x < pos.x || p.y < pos.y || p.x >= (pos.x + size.x) || p.y >= (pos.y + size.y)); - // } - - // constexpr bool contains(const geom2d::rect& r) const - // { - // return (r.pos.x >= pos.x) && (r.pos.x + r.size.x < pos.x + size.x) && - // (r.pos.y >= pos.y) && (r.pos.y + r.size.y < pos.y + size.y); - // } - - // constexpr bool overlaps(const geom2d::rect& r) const - // { - // return (pos.x < r.pos.x + r.size.x && pos.x + size.x >= r.pos.x && pos.y < r.pos.y + r.size.y && pos.y + size.y >= r.pos.y); - // } - //}; - template struct QuadTreeItemLocation { From 429f84fecede20901730aee92357fd4a1b1bd635 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Tue, 20 Sep 2022 21:49:56 +0100 Subject: [PATCH 08/10] Removed global variable from Quadtree --- utilities/olcUTIL_QuadTree.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/utilities/olcUTIL_QuadTree.h b/utilities/olcUTIL_QuadTree.h index 09ca105..636e5a2 100644 --- a/utilities/olcUTIL_QuadTree.h +++ b/utilities/olcUTIL_QuadTree.h @@ -58,7 +58,7 @@ #include "olcPixelGameEngine.h" #include "olcUTIL_Geometry2D.h" -constexpr size_t MAX_DEPTH = 8; + namespace olc::utils { @@ -73,10 +73,11 @@ namespace olc::utils class DynamicQuadTree { public: - DynamicQuadTree(const geom2d::rect& size, const size_t nDepth = 0) + DynamicQuadTree(const geom2d::rect& size, const size_t nDepth = 0, const size_t nMaxDepth = 8) { m_depth = nDepth; m_rect = size; + m_maxdepth = nMaxDepth; resize(m_rect); } @@ -88,13 +89,13 @@ namespace olc::utils if (geom2d::contains(m_rChild[i], itemsize)) { // Have we reached depth limit? - if (m_depth + 1 < MAX_DEPTH) + if (m_depth + 1 < m_maxdepth) { // No, so does child exist? if (!m_pChild[i]) { // No, so create it - m_pChild[i] = std::make_shared>(m_rChild[i], m_depth + 1); + m_pChild[i] = std::make_shared>(m_rChild[i], m_depth + 1, m_maxdepth); } // Yes, so add item to it @@ -183,7 +184,7 @@ namespace olc::utils { clear(); m_rect = rArea; - olc::vf2d vChildSize = m_rect.size / 2.0f; + olc::v2d_generic vChildSize = m_rect.size / CTYPE(2); m_rChild = { geom2d::rect(m_rect.pos, vChildSize), @@ -201,6 +202,7 @@ namespace olc::utils protected: size_t m_depth = 0; + size_t m_maxdepth = 8; // Area of this quadnode geom2d::rect m_rect; @@ -233,7 +235,7 @@ namespace olc::utils using IQuadtreeContainer = std::list>; public: - QuadTreeContainer(const geom2d::rect& size = { {0.0f, 0.0f}, { 100.0f, 100.0f } }, const size_t nDepth = 0) : root(size, nDepth) + QuadTreeContainer(const geom2d::rect& size = { {0.0f, 0.0f}, { 100.0f, 100.0f } }, const size_t nDepth = 0, const size_t nMaxDepth = 8) : root(size, nDepth, nMaxDepth) { } From c29acc74cc4632f89ed476604a649ae25910046b Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Tue, 20 Sep 2022 22:18:49 +0100 Subject: [PATCH 09/10] Added some more Geom2D functions, only 32 to go... --- utilities/olcUTIL_Geometry2D.h | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/utilities/olcUTIL_Geometry2D.h b/utilities/olcUTIL_Geometry2D.h index 0400598..cd13a29 100644 --- a/utilities/olcUTIL_Geometry2D.h +++ b/utilities/olcUTIL_Geometry2D.h @@ -496,8 +496,7 @@ namespace olc::utils::geom2d template inline constexpr bool contains(const circle& c1, const line& l) { - // TODO: - return false; + return contains(c1, l.start) && contains(c1, l.end); } // Check if triangle contains line segment @@ -637,8 +636,10 @@ namespace olc::utils::geom2d template inline constexpr bool contains(const circle& c, const rect& r) { - // TODO: - return false; + return contains(c, r.pos) + && contains(c, olc::v2d_generic{ r.pos.x + r.size.x, r.pos.y }) + && contains(c, olc::v2d_generic{ r.pos.x, r.pos.y + r.size.y }) + && contains(c, r.pos + r.size); } // Check if triangle contains rectangle @@ -680,8 +681,12 @@ namespace olc::utils::geom2d template inline constexpr bool overlaps(const circle& c, const rect& r) { - // TODO: - return false; + // Inspired by this (very clever btw) + // https://stackoverflow.com/questions/45370692/circle-rectangle-collision-response + // But modified to work :P + T2 overlap = (olc::v2d_generic{ std::clamp(c.pos.x, r.pos.x, r.pos.x + r.size.x), std::clamp(c.pos.y, r.pos.y, r.pos.y + r.size.y) } - c.pos).mag2(); + if (std::isnan(overlap)) overlap = T2(0); + return (overlap - (c.radius * c.radius)) < T2(0); } // Check if triangle overlaps rectangle From f467e7dce2930fe8fa7b3025668ec5902f964878 Mon Sep 17 00:00:00 2001 From: Javidx9 <25419386+OneLoneCoder@users.noreply.github.com> Date: Sat, 15 Oct 2022 14:10:07 +0100 Subject: [PATCH 10/10] PGE 2.20, Added Animate2D Utilities, and unleashed Geom2D as well, though work in progress --- examples/TEST_Animate2D.cpp | 278 +++++++++++++++++++++++++++ examples/TEST_QuickGUI.cpp | 226 ++++++++++++++++++++++ extensions/olcPGEX_QuickGUI.h | 170 ++++++++++++++-- extensions/olcPGEX_TransformedView.h | 17 +- olcPixelGameEngine.h | 47 ++++- utilities/olcUTIL_Animate2D.h | 212 ++++++++++++++++++++ utilities/olcUTIL_Geometry2D.h | 24 ++- 7 files changed, 947 insertions(+), 27 deletions(-) create mode 100644 examples/TEST_Animate2D.cpp create mode 100644 examples/TEST_QuickGUI.cpp create mode 100644 utilities/olcUTIL_Animate2D.h diff --git a/examples/TEST_Animate2D.cpp b/examples/TEST_Animate2D.cpp new file mode 100644 index 0000000..94197fa --- /dev/null +++ b/examples/TEST_Animate2D.cpp @@ -0,0 +1,278 @@ +/* + Example file for olcUTIL_Animate2D.h + + License (OLC-3) + ~~~~~~~~~~~~~~~ + + Copyright 2018 - 2022 OneLoneCoder.com + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions or derivations of source code must retain the above + copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions or derivative works in binary form must reproduce + the above copyright notice. This list of conditions and the following + disclaimer must be reproduced in the documentation and/or other + materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Links + ~~~~~ + YouTube: https://www.youtube.com/javidx9 + Discord: https://discord.gg/WhwHUMV + Twitter: https://www.twitter.com/javidx9 + Twitch: https://www.twitch.tv/javidx9 + GitHub: https://www.github.com/onelonecoder + Homepage: https://www.onelonecoder.com + + Author + ~~~~~~ + David Barr, aka javidx9, ŠOneLoneCoder 2019, 2020, 2021, 2022 + +*/ + + +#define OLC_PGE_APPLICATION +#include "olcPixelGameEngine.h" + +#include "utilities/olcUTIL_Animate2D.h" + +class TEST_Animate2D : public olc::PixelGameEngine +{ +public: + TEST_Animate2D() + { + sAppName = "Animate2D Utility Test"; + } + + // These are the states the dude can exist in, we'll need these for physics + // and animation. The physics side of things moves the dude around according to state, + // while the animator chooses the frames based upon state and time + enum class DudeState: uint8_t + { + WALK_N, WALK_S, WALK_E, WALK_W, IDLE_STAND, LAUGH, CHEER, YES, NO + }; + + // !! - IMPORTANT - !! + // The animation set + olc::utils::Animate2D::Animation animDude; + + // One big sprite containing all the graphics + olc::Renderable gfxAll; + + // Small object to reprsent a dude walking around doing things + struct sWalkingDude + { + // Which dude overall out of graophic? + int32_t id = 0; + // Where are they? + olc::vf2d pos; + // What are they doing? + DudeState UserState = DudeState::IDLE_STAND; + // For how long should they do it? + float fTick = 0.0f; + + // !! - IMPORTANT - !! + // Animation Token - links this object's state to animator. Note + // there is no 'ownership' or memory issues here, this is a token + // that the animator can use to quickly get to where it needs + // in order to return frames upon request for this object. + olc::utils::Animate2D::AnimationState animstate; + }; + + // Introducing.... The dudes! + size_t nDudes = 500; + std::vector vDudes; + +public: + bool OnUserCreate() override + { + // For this appliaction I have a single image that contains + // 28x2 unique characters, each character contains 8 animations of 3 + // frames each. Each frame is 26x36 pixels + gfxAll.Load("./assets/MegaSprite1.png"); + + // Thats A LOT of individual graphics, but they all follow a similar pattern + // because the asset was created usefully (take note certain popular asset creators) + // which means we can reuse the animation without needing to define it + // individually for all the "dudes" - the "cookie cutter" approach + + // Manually construct sequences - gfxAll could in fact be nullptr for this + // application, but ive kept it here for convenience + olc::utils::Animate2D::FrameSequence anim_fs_walk_s; + anim_fs_walk_s.AddFrame({ &gfxAll, {{0,0}, {26,36}} }); + anim_fs_walk_s.AddFrame({ &gfxAll, {{26,0}, {26,36}} }); + anim_fs_walk_s.AddFrame({ &gfxAll, {{52,0}, {26,36}} }); + + olc::utils::Animate2D::FrameSequence anim_fs_walk_w; + anim_fs_walk_w.AddFrame({ &gfxAll, {{ 0,36}, {26,36}} }); + anim_fs_walk_w.AddFrame({ &gfxAll, {{26,36}, {26,36}} }); + anim_fs_walk_w.AddFrame({ &gfxAll, {{52,36}, {26,36}} }); + + olc::utils::Animate2D::FrameSequence anim_fs_walk_e; + anim_fs_walk_e.AddFrame({ &gfxAll, {{ 0,72}, {26,36}} }); + anim_fs_walk_e.AddFrame({ &gfxAll, {{26,72}, {26,36}} }); + anim_fs_walk_e.AddFrame({ &gfxAll, {{52,72}, {26,36}} }); + + olc::utils::Animate2D::FrameSequence anim_fs_walk_n; + anim_fs_walk_n.AddFrame({ &gfxAll, {{ 0,108}, {26,36}} }); + anim_fs_walk_n.AddFrame({ &gfxAll, {{26,108}, {26,36}} }); + anim_fs_walk_n.AddFrame({ &gfxAll, {{52,108}, {26,36}} }); + + olc::utils::Animate2D::FrameSequence anim_fs_yes; + anim_fs_yes.AddFrame({ &gfxAll, {{ 0,144}, {26,36}} }); + anim_fs_yes.AddFrame({ &gfxAll, {{26,144}, {26,36}} }); + anim_fs_yes.AddFrame({ &gfxAll, {{52,144}, {26,36}} }); + + olc::utils::Animate2D::FrameSequence anim_fs_no; + anim_fs_no.AddFrame({ &gfxAll, {{ 0,180}, {26,36}} }); + anim_fs_no.AddFrame({ &gfxAll, {{26,180}, {26,36}} }); + anim_fs_no.AddFrame({ &gfxAll, {{52,180}, {26,36}} }); + + olc::utils::Animate2D::FrameSequence anim_fs_laugh; + anim_fs_laugh.AddFrame({ &gfxAll, {{ 0,216}, {26,36}} }); + anim_fs_laugh.AddFrame({ &gfxAll, {{26,216}, {26,36}} }); + anim_fs_laugh.AddFrame({ &gfxAll, {{52,216}, {26,36}} }); + + olc::utils::Animate2D::FrameSequence anim_fs_cheer; + anim_fs_cheer.AddFrame({ &gfxAll, {{ 0,252}, {26,36}} }); + anim_fs_cheer.AddFrame({ &gfxAll, {{26,252}, {26,36}} }); + anim_fs_cheer.AddFrame({ &gfxAll, {{52,252}, {26,36}} }); + + // A special "idle" animation just consists of a single frame + olc::utils::Animate2D::FrameSequence anim_fs_idle; + anim_fs_idle.AddFrame({ &gfxAll, {{26,0}, {26,36}} }); + + // We have constructed teh individual sequences, now its time + // to add them to an animation, along with a state name/enum. + // + // I have chosen to use the enum shown earlier. You could use + // std::string too, which is conveninent if you need to display + // the states, though potentially far less performant + + animDude.AddState(DudeState::WALK_S, anim_fs_walk_s); + animDude.AddState(DudeState::WALK_W, anim_fs_walk_w); + animDude.AddState(DudeState::WALK_E, anim_fs_walk_e); + animDude.AddState(DudeState::WALK_N, anim_fs_walk_n); + animDude.AddState(DudeState::IDLE_STAND, anim_fs_idle); + animDude.AddState(DudeState::YES, anim_fs_yes); + animDude.AddState(DudeState::NO, anim_fs_no); + animDude.AddState(DudeState::LAUGH, anim_fs_laugh); + animDude.AddState(DudeState::CHEER, anim_fs_cheer); + + // Initialise the dudes + for (size_t n = 0; n < nDudes; n++) + { + sWalkingDude dude; + + // Random dude + dude.id = rand() % (28 * 2); + + // Begin in idle state, at random location + dude.UserState = DudeState::IDLE_STAND; + dude.pos = { float(rand() % ScreenWidth()), float(rand() % ScreenHeight()) }; + + // The animation token needs to be updated too + animDude.ChangeState(dude.animstate, dude.UserState); + + vDudes.push_back(dude); + } + return true; + } + + bool OnUserUpdate(float fElapsedTime) override + { + // Update Dudes + float fSpeed = 32.0f; + + for (auto& dude : vDudes) + { + // If a dude's tick reaches 0, it will select a new state + dude.fTick -= fElapsedTime; + if (dude.fTick < 0.0f) + { + // Choose one out of the 9 randomly + int nAction = rand() % 9; + + // Choose for how long it should do it in seconds + dude.fTick = (float(rand()) / float(RAND_MAX)) * 5.0f; + + // Assign the state depending on the dice roll - (since enum + // we could cast here too, but, well, meh...) + if (nAction == 0) dude.UserState = DudeState::IDLE_STAND; + if (nAction == 1) dude.UserState = DudeState::WALK_S; + if (nAction == 2) dude.UserState = DudeState::WALK_N; + if (nAction == 3) dude.UserState = DudeState::WALK_E; + if (nAction == 4) dude.UserState = DudeState::WALK_W; + if (nAction == 5) dude.UserState = DudeState::YES; + if (nAction == 6) dude.UserState = DudeState::NO; + if (nAction == 7) dude.UserState = DudeState::LAUGH; + if (nAction == 8) dude.UserState = DudeState::CHEER; + + // State has changed, so update animation token + // !! - IMPORTANT - !! + animDude.ChangeState(dude.animstate, dude.UserState); + } + + // Update "physics", if walking move in that direction at speed + if (dude.UserState == DudeState::WALK_S) dude.pos += olc::vf2d(0, +1) * fSpeed * fElapsedTime; + if (dude.UserState == DudeState::WALK_N) dude.pos += olc::vf2d(0, -1) * fSpeed * fElapsedTime; + if (dude.UserState == DudeState::WALK_E) dude.pos += olc::vf2d(+1, 0) * fSpeed * fElapsedTime; + if (dude.UserState == DudeState::WALK_W) dude.pos += olc::vf2d(-1, 0) * fSpeed * fElapsedTime; + + // If walk off screen, wrap around to other side + if (dude.pos.x > ScreenWidth()) dude.pos.x -= ScreenWidth(); + if (dude.pos.y > ScreenHeight()) dude.pos.x -= ScreenHeight(); + if (dude.pos.x < 0) dude.pos.x += ScreenWidth(); + if (dude.pos.y < 0) dude.pos.y += ScreenHeight(); + + // Update animation token every frame + // !! - IMPORTANT - !! + animDude.UpdateState(dude.animstate, fElapsedTime); + } + + // Render Dudes + for (const auto& dude : vDudes) + { + // Get the frame, this contains both source image and source location rectangle + // !! - IMPORTANT - !! + const auto& frame = animDude.GetFrame(dude.animstate); + + // Thats cool, but there are 28x2 dudes on the sprite sheet, so using the ID, construct + // an offset to the correct dude + olc::vi2d vOffset = { (dude.id % 28) * 78, (dude.id / 28) * 288 }; + + // Use DrawPartialDecal to chop out the correct dude frm the image source + DrawPartialDecal(dude.pos, frame.GetSourceImage()->Decal(), frame.GetSourceRect().pos + vOffset, frame.GetSourceRect().size); + } + + // That's it + return true; + } +}; + +int main() +{ + TEST_Animate2D demo; + if (demo.Construct(640, 480, 2, 2)) + demo.Start(); + return 0; +} \ No newline at end of file diff --git a/examples/TEST_QuickGUI.cpp b/examples/TEST_QuickGUI.cpp new file mode 100644 index 0000000..2e8761b --- /dev/null +++ b/examples/TEST_QuickGUI.cpp @@ -0,0 +1,226 @@ +/* + Example file for olcPGEX_QuickGUI.h + + License (OLC-3) + ~~~~~~~~~~~~~~~ + + Copyright 2018 - 2022 OneLoneCoder.com + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions or derivations of source code must retain the above + copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions or derivative works in binary form must reproduce + the above copyright notice. This list of conditions and the following + disclaimer must be reproduced in the documentation and/or other + materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Links + ~~~~~ + YouTube: https://www.youtube.com/javidx9 + Discord: https://discord.gg/WhwHUMV + Twitter: https://www.twitter.com/javidx9 + Twitch: https://www.twitch.tv/javidx9 + GitHub: https://www.github.com/onelonecoder + Homepage: https://www.onelonecoder.com + + Author + ~~~~~~ + David Barr, aka javidx9, ŠOneLoneCoder 2019, 2020, 2021, 2022 + +*/ + +#define OLC_PGE_APPLICATION +#include "olcPixelGameEngine.h" + +// PGEX Require the presence of olc::PixelGameEngine +#define OLC_PGEX_QUICKGUI +#include "extensions/olcPGEX_QuickGUI.h" + + +class olcDemo_QuickGUI : public olc::PixelGameEngine +{ +public: + olcDemo_QuickGUI() + { + sAppName = "olcDemo_QuickGUI"; + } + +protected: + olc::QuickGUI::Manager guiManager; + + olc::QuickGUI::Slider* guiSlider1 = nullptr; + olc::QuickGUI::Slider* guiSlider2 = nullptr; + olc::QuickGUI::Slider* guiSlider3 = nullptr; + + olc::QuickGUI::Button* guiButton1 = nullptr; + olc::QuickGUI::Button* guiButton2 = nullptr; + olc::QuickGUI::Button* guiButton3 = nullptr; + + olc::QuickGUI::Slider* guiThemeColourR = nullptr; + olc::QuickGUI::Slider* guiThemeColourG = nullptr; + olc::QuickGUI::Slider* guiThemeColourB = nullptr; + + olc::QuickGUI::Label* guiLabelR = nullptr; + olc::QuickGUI::Label* guiLabelG = nullptr; + olc::QuickGUI::Label* guiLabelB = nullptr; + + olc::QuickGUI::CheckBox* guiCheckBox1 = nullptr; + + olc::QuickGUI::TextBox* guiTextBox1 = nullptr; + olc::QuickGUI::TextBox* guiTextBox2 = nullptr; + + + std::vector listExample; + olc::QuickGUI::ListBox* guiListBox = nullptr; + +public: + bool OnUserCreate() override + { + // Horizontal Slider + guiSlider1 = new olc::QuickGUI::Slider(guiManager, + { 30.0f, 10.0f }, { 246.0f, 10.0f }, 0, 100, 50); + // Diagonal Slider! + guiSlider2 = new olc::QuickGUI::Slider(guiManager, + { 20.0f, 20.0f }, { 120.0f, 120.0f }, 0, 100, 50); + // Vertical Slider + guiSlider3 = new olc::QuickGUI::Slider(guiManager, + { 10.0f, 30.0f }, { 10.0f, 230.0f }, 0, 100, 50); + + // Theme colour slider - Red + guiThemeColourR = new olc::QuickGUI::Slider(guiManager, + { 150.0f, 30.0f }, { 246.0f, 30.0f }, 0, 255, 0); + // Theme colour slider - Green + guiThemeColourG = new olc::QuickGUI::Slider(guiManager, + { 150.0f, 50.0f }, { 246.0f, 50.0f }, 0, 255, 0); + // Theme colour slider - Blue + guiThemeColourB = new olc::QuickGUI::Slider(guiManager, + { 150.0f, 70.0f }, { 246.0f, 70.0f }, 0, 255, 128); + + // Labels for theme colour sliders + guiLabelR = new olc::QuickGUI::Label(guiManager, + "Red:", { 80.0f, 22.0f }, { 50.0f, 16.0f }); + guiLabelG = new olc::QuickGUI::Label(guiManager, + "Green:", { 80.0f, 42.0f }, { 50.0f, 16.0f }); + guiLabelB = new olc::QuickGUI::Label(guiManager, + "Blue:", { 80.0f, 62.0f }, { 50.0f, 16.0f }); + + // Customize how the labels look + guiLabelB->nAlign = olc::QuickGUI::Label::Alignment::Right; + guiLabelG->nAlign = olc::QuickGUI::Label::Alignment::Right; + guiLabelG->bHasBorder = true; + guiLabelR->nAlign = olc::QuickGUI::Label::Alignment::Right; + guiLabelR->bHasBorder = true; + guiLabelR->bHasBackground = true; + + // Some Buttons, 1 is just a thing, 2 has its text updated and 3 resets the theme + guiButton1 = new olc::QuickGUI::Button(guiManager, + "Button 1", { 30.0f, 150.0f }, { 100.0f, 16.0f }); + guiButton2 = new olc::QuickGUI::Button(guiManager, + "Button 2", { 30.0f, 170.0f }, { 100.0f, 16.0f }); + guiButton3 = new olc::QuickGUI::Button(guiManager, + "Reset Theme", { 30.0f, 190.0f }, { 100.0f, 16.0f }); + + // A CheckBox, switches between sprite or decal drawing + guiCheckBox1 = new olc::QuickGUI::CheckBox(guiManager, + "Use Decals", false, { 30.0f, 210.0f }, { 100.0f, 16.0f }); + + // TextBox, allows basic text entry + guiTextBox1 = new olc::QuickGUI::TextBox(guiManager, + "", { 150.0f, 140.0f }, { 100.0f, 16.0f }); + guiTextBox2 = new olc::QuickGUI::TextBox(guiManager, + "0.04", { 150.0f, 160.0f }, { 100.0f, 16.0f }); + + listExample.push_back("Item 1"); + listExample.push_back("Item 2"); + listExample.push_back("Item 3"); + listExample.push_back("Item 4"); + listExample.push_back("Item 5"); + listExample.push_back("Item 6"); + + guiListBox = new olc::QuickGUI::ListBox(guiManager, + listExample, { 150.0f, 180.0f }, { 100.0f, 54.0f }); + + // but but but.... waaaaaaaaahaaaaaaaa.... where do I delete these horrible + // pointers??? I just can't accept that addressable memory exists and it makes + // me feel really insecure! + // + // By default olc::QuickGUI::Manager will delete any Controls added to it, so you + // dont have to. If you must obfuscate your program with smart pointers, or require + // that you are in rage-control of your memory at all times, construct the Manager + // with false as the argument - then its all up to you buddy. + + return true; + } + + bool OnUserUpdate(float fElapsedTime) override + { + // We must update the manager at some point each frame. Values of controls + // are only valid AFTER this call to update() + guiManager.Update(this); + + // Some silly examples... + + // 001) The "theme colour" can be set from slider values + guiManager.colNormal = olc::Pixel( + uint8_t(guiThemeColourR->fValue), + uint8_t(guiThemeColourG->fValue), + uint8_t(guiThemeColourB->fValue)); + + // 002) Display Slider 1 value on Button 2 + guiButton2->sText = "Button " + std::to_string(int32_t(guiSlider1->fValue)); + + // 003) Check if "Reset Theme" button is pressed, if it is, well, err... + if (guiButton3->bPressed) + { + // ...reset the theme! (which also updates the sliders) + guiThemeColourR->fValue = 0.0f; + guiThemeColourG->fValue = 0.0f; + guiThemeColourB->fValue = 128.0f; + } + + // 004) Link Slider 2 and Slider 3 together + if(guiSlider2->bHeld) + guiSlider3->fValue = 100.0f - guiSlider2->fValue; + if (guiSlider3->bHeld) + guiSlider2->fValue = 100.0f - guiSlider3->fValue; + + + // Draw Stuff! + Clear(olc::BLACK); + + // 005) Use checkbox to determine rendering mode + if (guiCheckBox1->bChecked) + guiManager.DrawDecal(this); + else + guiManager.Draw(this); + return true; + } +}; + +int main() +{ + olcDemo_QuickGUI demo; + if (demo.Construct(256, 240, 4, 4)) + demo.Start(); + return 0; +} \ No newline at end of file diff --git a/extensions/olcPGEX_QuickGUI.h b/extensions/olcPGEX_QuickGUI.h index cbf5647..e7c55ab 100644 --- a/extensions/olcPGEX_QuickGUI.h +++ b/extensions/olcPGEX_QuickGUI.h @@ -1,5 +1,5 @@ /* - OneLoneCoder - QuickGUI v1.00 + OneLoneCoder - QuickGUI v1.01 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A semi-immediate mode GUI for very simple GUI stuff. Includes: @@ -55,6 +55,12 @@ ~~~~~~ David Barr, aka javidx9, ŠOneLoneCoder 2019, 2020, 2021, 2022 + Changes + ~~~~~~~ + v1.01 +Moved Slider::fGrabRad into "theme" + +Manager::CopyThemeFrom() - copies theme attributes from a different manager + +ListBox - Displays a vector of strings + */ #ifndef OLC_PGEX_QUICKGUI_H @@ -143,6 +149,10 @@ namespace olc::QuickGUI float fHoverSpeedOn = 10.0f; // Speed to transiton from Hover -> Normal float fHoverSpeedOff = 4.0f; + // Size of grab handle + float fGrabRad = 8.0f; + // Copy all theme attributes into a different manager object + void CopyThemeFrom(const Manager& manager); private: // Should this manager call delete on the controls it opeerates? @@ -261,8 +271,7 @@ namespace olc::QuickGUI float fMax = +100.0f; // Current value float fValue = 0.0f; - // Size of grab handle - float fGrabRad = 8.0f; + // Location of minimum/start olc::vf2d vPosMin; // Location of maximum/end @@ -274,6 +283,38 @@ namespace olc::QuickGUI void DrawDecal(olc::PixelGameEngine* pge) override; }; + + class ListBox : public BaseControl + { + public: + ListBox(olc::QuickGUI::Manager& manager, // Associate with a Manager + std::vector& vList, + const olc::vf2d& pos, // Location of list top-left + const olc::vf2d& size); // Size of list + + // Position of list + olc::vf2d vPos; + // Size of list + olc::vf2d vSize; + // Show a border? + bool bHasBorder = true; + // Show a background? + bool bHasBackground = true; + + public: + Slider *m_pSlider = nullptr; + Manager m_group; + size_t m_nVisibleItems = 0; + std::vector& m_vList; + + public: + size_t nSelectedItem = 0; + + public: // BaseControl overrides + void Update(olc::PixelGameEngine* pge) override; + void Draw(olc::PixelGameEngine* pge) override; + void DrawDecal(olc::PixelGameEngine* pge) override; + }; } @@ -333,6 +374,19 @@ namespace olc::QuickGUI { for (auto& p : m_vControls) p->DrawDecal(pge); } + + void Manager::CopyThemeFrom(const Manager& manager) + { + this->colBorder = manager.colBorder; + this->colClick = manager.colClick; + this->colDisable = manager.colDisable; + this->colHover = manager.colHover; + this->colNormal = manager.colNormal; + this->colText = manager.colText; + this->fGrabRad = manager.fGrabRad; + this->fHoverSpeedOff = manager.fHoverSpeedOff; + this->fHoverSpeedOn = manager.fHoverSpeedOn; + } #pragma endregion #pragma region Label @@ -694,7 +748,7 @@ namespace olc::QuickGUI else { olc::vf2d vSliderPos = vPosMin + (vPosMax - vPosMin) * ((fValue - fMin) / (fMax - fMin)); - if ((vMouse - vSliderPos).mag2() <= int32_t(fGrabRad) * int32_t(fGrabRad)) + if ((vMouse - vSliderPos).mag2() <= int32_t(m_manager.fGrabRad) * int32_t(m_manager.fGrabRad)) { m_fTransition += fElapsedTime * m_manager.fHoverSpeedOn; m_state = State::Hover; @@ -736,19 +790,19 @@ namespace olc::QuickGUI switch (m_state) { case State::Disabled: - pge->FillCircle(vSliderPos, int32_t(fGrabRad), m_manager.colDisable); + pge->FillCircle(vSliderPos, int32_t(m_manager.fGrabRad), m_manager.colDisable); break; case State::Normal: case State::Hover: - pge->FillCircle(vSliderPos, int32_t(fGrabRad), olc::PixelLerp(m_manager.colNormal, m_manager.colHover, m_fTransition)); + pge->FillCircle(vSliderPos, int32_t(m_manager.fGrabRad), olc::PixelLerp(m_manager.colNormal, m_manager.colHover, m_fTransition)); break; case State::Click: - pge->FillCircle(vSliderPos, int32_t(fGrabRad), m_manager.colClick); + pge->FillCircle(vSliderPos, int32_t(m_manager.fGrabRad), m_manager.colClick); break; } - pge->DrawCircle(vSliderPos, int32_t(fGrabRad), m_manager.colBorder); + pge->DrawCircle(vSliderPos, int32_t(m_manager.fGrabRad), m_manager.colBorder); } void Slider::DrawDecal(olc::PixelGameEngine* pge) @@ -762,25 +816,117 @@ namespace olc::QuickGUI switch (m_state) { case State::Disabled: - pge->FillRectDecal(vSliderPos - olc::vf2d(fGrabRad, fGrabRad), olc::vf2d(fGrabRad, fGrabRad) * 2.0f, m_manager.colDisable); + pge->FillRectDecal(vSliderPos - olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad), olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad) * 2.0f, m_manager.colDisable); break; case State::Normal: case State::Hover: - pge->FillRectDecal(vSliderPos - olc::vf2d(fGrabRad, fGrabRad), olc::vf2d(fGrabRad, fGrabRad) * 2.0f, olc::PixelLerp(m_manager.colNormal, m_manager.colHover, m_fTransition)); + pge->FillRectDecal(vSliderPos - olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad), olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad) * 2.0f, olc::PixelLerp(m_manager.colNormal, m_manager.colHover, m_fTransition)); break; case State::Click: - pge->FillRectDecal(vSliderPos - olc::vf2d(fGrabRad, fGrabRad), olc::vf2d(fGrabRad, fGrabRad) * 2.0f, m_manager.colClick); + pge->FillRectDecal(vSliderPos - olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad), olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad) * 2.0f, m_manager.colClick); break; } pge->SetDecalMode(olc::DecalMode::WIREFRAME); - pge->FillRectDecal(vSliderPos - olc::vf2d(fGrabRad, fGrabRad), olc::vf2d(fGrabRad, fGrabRad) * 2.0f, m_manager.colBorder); + pge->FillRectDecal(vSliderPos - olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad), olc::vf2d(m_manager.fGrabRad, m_manager.fGrabRad) * 2.0f, m_manager.colBorder); pge->SetDecalMode(olc::DecalMode::NORMAL); } #pragma endregion +#pragma region ListBox + ListBox::ListBox(olc::QuickGUI::Manager& manager, std::vector& vList, const olc::vf2d& pos, const olc::vf2d& size) + : BaseControl(manager), m_vList(vList) + { + m_group.CopyThemeFrom(m_manager); + vPos = pos; + vSize = size; + m_pSlider = new Slider(m_group, { pos.x + size.x - m_manager.fGrabRad - 1, pos.y + m_manager.fGrabRad + 1 }, + { pos.x + size.x - m_manager.fGrabRad - 1, pos.y + size.y - m_manager.fGrabRad - 1 }, 0, float(m_vList.size()), 0); + } + + void ListBox::Update(olc::PixelGameEngine* pge) + { + if (m_state == State::Disabled || !bVisible) + return; + + olc::vf2d vMouse = pge->GetMousePos() - vPos + olc::vi2d(2,0); + if (pge->GetMouse(olc::Mouse::LEFT).bPressed) + { + if (vMouse.x >= 0 && vMouse.x < vSize.x - (m_group.fGrabRad * 2) && vMouse.y >= 0 && vMouse.y < vSize.y) + { + nSelectedItem = size_t(m_pSlider->fValue + vMouse.y / 10); + } + } + + nSelectedItem = std::clamp(nSelectedItem, size_t(0), m_vList.size()-1); + + m_pSlider->fMax = float(m_vList.size()); + m_group.Update(pge); + } + + void ListBox::Draw(olc::PixelGameEngine* pge) + { + if (!bVisible) + return; + + if (bHasBackground) + { + pge->FillRect(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colNormal); + } + + if (bHasBorder) + pge->DrawRect(vPos, vSize - olc::vf2d(1, 1), m_manager.colBorder); + + + size_t idx0 = size_t(m_pSlider->fValue); + size_t idx1 = std::min(idx0 + size_t((vSize.y - 4) / 10), m_vList.size()); + + olc::vf2d vTextPos = vPos + olc::vf2d(2,2); + for (size_t idx = idx0; idx < idx1; idx++) + { + if (idx == nSelectedItem) + pge->FillRect(vTextPos - olc::vi2d(1,1), {int32_t(vSize.x - m_group.fGrabRad * 2), 10}, m_group.colHover); + pge->DrawStringProp(vTextPos, m_vList[idx]); + vTextPos.y += 10; + } + + m_group.Draw(pge); + } + + void ListBox::DrawDecal(olc::PixelGameEngine* pge) + { + if (!bVisible) + return; + + if (bHasBackground) + pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colNormal); + + size_t idx0 = size_t(m_pSlider->fValue); + size_t idx1 = std::min(idx0 + size_t((vSize.y - 4) / 10), m_vList.size()); + + olc::vf2d vTextPos = vPos + olc::vf2d(2, 2); + for (size_t idx = idx0; idx < idx1; idx++) + { + if (idx == nSelectedItem) + pge->FillRectDecal(vTextPos - olc::vi2d(1, 1), { vSize.x - m_group.fGrabRad * 2.0f, 10.0f }, m_group.colHover); + pge->DrawStringPropDecal(vTextPos, m_vList[idx]); + vTextPos.y += 10; + } + + if (bHasBorder) + { + pge->SetDecalMode(olc::DecalMode::WIREFRAME); + pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colBorder); + pge->SetDecalMode(olc::DecalMode::NORMAL); + } + + m_group.DrawDecal(pge); + } +#pragma endregion + + } #endif // OLC_PGEX_QUICKGUI #endif // OLC_PGEX_QUICKGUI_H \ No newline at end of file diff --git a/extensions/olcPGEX_TransformedView.h b/extensions/olcPGEX_TransformedView.h index d5641ca..51bdc7b 100644 --- a/extensions/olcPGEX_TransformedView.h +++ b/extensions/olcPGEX_TransformedView.h @@ -3,7 +3,7 @@ +-------------------------------------------------------------+ | OneLoneCoder Pixel Game Engine Extension | - | Transformed View v1.06 | + | Transformed View v1.07 | +-------------------------------------------------------------+ NOTE: UNDER ACTIVE DEVELOPMENT - THERE ARE BUGS/GLITCHES @@ -71,6 +71,8 @@ 1.04: Added DrawPolygonDecal() for arbitrary polygons 1.05: Clipped DrawSprite() to visible area, massive performance increase 1.06: Fixed error in DrawLine() - Thanks CraisyDaisyRecords (& Fern)! + 1.07: +DrawRectDecal() + +GetPGE() */ #pragma once @@ -89,6 +91,8 @@ namespace olc TransformedView() = default; virtual void Initialise(const olc::vi2d& vViewArea, const olc::vf2d& vPixelScale = { 1.0f, 1.0f }); + olc::PixelGameEngine* GetPGE(); + public: void SetWorldOffset(const olc::vf2d& vOffset); void MoveWorldOffset(const olc::vf2d& vDeltaOffset); @@ -179,6 +183,8 @@ namespace olc void DrawStringPropDecal(const olc::vf2d& pos, const std::string& sText, const olc::Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }); // Draws a single shaded filled rectangle as a decal void FillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col = olc::WHITE); + void DrawRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col = olc::WHITE); + // Draws a corner shaded rectangle as a decal void GradientFillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel colTL, const olc::Pixel colBL, const olc::Pixel colBR, const olc::Pixel colTR); // Draws an arbitrary convex textured polygon using GPU @@ -219,6 +225,10 @@ namespace olc namespace olc { + olc::PixelGameEngine* TransformedView::GetPGE() + { + return pge; + } void TransformedView::Initialise(const olc::vi2d& vViewArea, const olc::vf2d& vPixelScale) { @@ -627,6 +637,11 @@ namespace olc pge->FillRectDecal(WorldToScreen(pos), (size * m_vWorldScale).ceil(), col); } + void TransformedView::DrawRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) + { + pge->DrawRectDecal(WorldToScreen(pos), (size * m_vWorldScale).ceil(), col); + } + void TransformedView::DrawLineDecal(const olc::vf2d& pos1, const olc::vf2d& pos2, Pixel p) { pge->DrawLineDecal(WorldToScreen(pos1), WorldToScreen(pos2), p); diff --git a/olcPixelGameEngine.h b/olcPixelGameEngine.h index b4b1b92..17b2d4a 100644 --- a/olcPixelGameEngine.h +++ b/olcPixelGameEngine.h @@ -3,7 +3,7 @@ olcPixelGameEngine.h +-------------------------------------------------------------+ - | OneLoneCoder Pixel Game Engine v2.19 | + | OneLoneCoder Pixel Game Engine v2.20 | | "What do you need? Pixels... Lots of Pixels..." - javidx9 | +-------------------------------------------------------------+ @@ -187,6 +187,8 @@ AlterEgo...........Final Fantasy XII - The Zodiac Age SlicEnDicE.........Noita, Inside TGD................Voucher Gift + Dragoneye..........Lucas Arts Adventure Game Pack + Anonymous Pirate...Return To Monkey Island Special thanks to my Patreons too - I wont name you on here, but I've certainly enjoyed my tea and flapjacks :D @@ -297,8 +299,10 @@ +ConsoleClear() - Clears built in command console output +ConsoleOut() - Stream strings to command console output +ConsoleCaptureStdOut() - Capture std::cout by redirecting to built-in console - +IsConsoleShowing() - Returns true if console is currently active +OnConsoleCommand() - Override is called when command is entered into built in console + 2.20: +DrawRectDecal() - Keeps OneSketchyGuy quiet + +GetScreenSize() + +olc::Sprite::Size() - returns size of sprite in vector format !! Apple Platforms will not see these updates immediately - Sorry, I dont have a mac to test... !! !! Volunteers willing to help appreciated, though PRs are manually integrated with credit !! @@ -378,7 +382,7 @@ int main() #include #pragma endregion -#define PGE_VER 219 +#define PGE_VER 220 // O------------------------------------------------------------------------------O // | COMPILER CONFIGURATION ODDITIES | @@ -771,6 +775,7 @@ namespace olc Pixel* GetData(); olc::Sprite* Duplicate(); olc::Sprite* Duplicate(const olc::vi2d& vPos, const olc::vi2d& vSize); + olc::vi2d Size() const; std::vector pColData; Mode modeSample = Mode::NORMAL; @@ -980,6 +985,8 @@ namespace olc const olc::vi2d& GetPixelSize() const; // Gets actual pixel scale const olc::vi2d& GetScreenPixelSize() const; + // Gets "screen" size + const olc::vi2d& GetScreenSize() const; public: // CONFIGURATION ROUTINES // Layer targeting functions @@ -1074,6 +1081,7 @@ namespace olc void DrawStringDecal(const olc::vf2d& pos, const std::string& sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }); void DrawStringPropDecal(const olc::vf2d& pos, const std::string& sText, const Pixel col = olc::WHITE, const olc::vf2d& scale = { 1.0f, 1.0f }); // Draws a single shaded filled rectangle as a decal + void DrawRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col = olc::WHITE); void FillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col = olc::WHITE); // Draws a corner shaded rectangle as a decal void GradientFillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel colTL, const olc::Pixel colBL, const olc::Pixel colBR, const olc::Pixel colTR); @@ -1506,6 +1514,11 @@ namespace olc return spr; } + olc::vi2d olc::Sprite::Size() const + { + return { width, height }; + } + // O------------------------------------------------------------------------------O // | olc::Decal IMPLEMENTATION | // O------------------------------------------------------------------------------O @@ -1942,6 +1955,9 @@ namespace olc const olc::vi2d& PixelGameEngine::GetScreenPixelSize() const { return vScreenPixelSize; } + const olc::vi2d& PixelGameEngine::GetScreenSize() const + { return vScreenSize; } + const olc::vi2d& PixelGameEngine::GetWindowMouse() const { return vMouseWindowPos; } @@ -2665,7 +2681,12 @@ namespace olc void PixelGameEngine::DrawLineDecal(const olc::vf2d& pos1, const olc::vf2d& pos2, Pixel p) { - DecalInstance di; + auto m = nDecalMode; + nDecalMode = olc::DecalMode::WIREFRAME; + DrawPolygonDecal(nullptr, { pos1, pos2 }, { {0, 0}, {0,0} }, p); + nDecalMode = m; + + /*DecalInstance di; di.decal = nullptr; di.points = uint32_t(2); di.pos.resize(di.points); @@ -2681,12 +2702,26 @@ namespace olc di.tint[1] = p; di.w[1] = 1.0f; di.mode = olc::DecalMode::WIREFRAME; - vLayers[nTargetLayer].vecDecalInstance.push_back(di); + di.structure = nDecalStructure; + vLayers[nTargetLayer].vecDecalInstance.push_back(di);*/ + } + + void PixelGameEngine::DrawRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) + { + auto m = nDecalMode; + SetDecalMode(olc::DecalMode::WIREFRAME); + olc::vf2d vNewSize = size;// (size - olc::vf2d(0.375f, 0.375f)).ceil(); + std::array points = { { {pos}, {pos.x, pos.y + vNewSize.y}, {pos + vNewSize}, {pos.x + vNewSize.x, pos.y} } }; + std::array uvs = { {{0,0},{0,0},{0,0},{0,0}} }; + std::array cols = { {col, col, col, col} }; + DrawExplicitDecal(nullptr, points.data(), uvs.data(), cols.data(), 4); + SetDecalMode(m); + } void PixelGameEngine::FillRectDecal(const olc::vf2d& pos, const olc::vf2d& size, const olc::Pixel col) { - olc::vf2d vNewSize = (size - olc::vf2d(0.375f, 0.375f)).ceil(); + olc::vf2d vNewSize = size;// (size - olc::vf2d(0.375f, 0.375f)).ceil(); std::array points = { { {pos}, {pos.x, pos.y + vNewSize.y}, {pos + vNewSize}, {pos.x + vNewSize.x, pos.y} } }; std::array uvs = { {{0,0},{0,0},{0,0},{0,0}} }; std::array cols = { {col, col, col, col} }; diff --git a/utilities/olcUTIL_Animate2D.h b/utilities/olcUTIL_Animate2D.h new file mode 100644 index 0000000..52ae051 --- /dev/null +++ b/utilities/olcUTIL_Animate2D.h @@ -0,0 +1,212 @@ +/* + OneLoneCoder - Animate2D v1.00 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Handles animated Sprites efficiently + + + License (OLC-3) + ~~~~~~~~~~~~~~~ + + Copyright 2018 - 2022 OneLoneCoder.com + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions or derivations of source code must retain the above + copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions or derivative works in binary form must reproduce + the above copyright notice. This list of conditions and the following + disclaimer must be reproduced in the documentation and/or other + materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Links + ~~~~~ + YouTube: https://www.youtube.com/javidx9 + Discord: https://discord.gg/WhwHUMV + Twitter: https://www.twitter.com/javidx9 + Twitch: https://www.twitch.tv/javidx9 + GitHub: https://www.github.com/onelonecoder + Homepage: https://www.onelonecoder.com + + Author + ~~~~~~ + David Barr, aka javidx9, ŠOneLoneCoder 2019, 2020, 2021, 2022 + +*/ + +#pragma once + +#include "olcPixelGameEngine.h" +#include "utilities/olcUTIL_Geometry2D.h" + +namespace olc::utils::Animate2D +{ + // This class rerpresents a valid "frame" of an animation. It could be from any image source, and + // any location withing that image source. Once it's constructed, it's advised not to change it, as + // this likely indicates a usage bug. + // + // "Sourceless" frames are valid too - this is useful if you have a particular animation set, but + // want to apply it to a variety of sources, for example sprite maps with common layouts. + class Frame + { + public: + inline Frame(const olc::Renderable* gfxSource, const geom2d::rect& rectSource = { {0,0},{0,0} }) + : gfxImageSource(gfxSource), rectFrameSource(rectSource) + { + // If no source rectangle specified then use whole image source. Ignore in the event + // that a frame is set up as source-less + if(gfxSource && rectFrameSource.size.x == 0) + rectFrameSource.size = gfxSource->Sprite()->Size(); + } + + inline const olc::Renderable* GetSourceImage() const + { + return gfxImageSource; + } + + inline const geom2d::rect& GetSourceRect() const + { + return rectFrameSource; + } + + private: + const olc::Renderable* gfxImageSource; + geom2d::rect rectFrameSource; + }; + + // Animation styles decide how the frames should be traversed in time + enum class Style : uint8_t + { + Repeat, // Cycle through, go back to beginning + OneShot, // Play once and suspend on final frame + PingPong, // Traverse through forwards, then backwards + Reverse, // Cycle through sequence backwards + }; + + class FrameSequence + { + public: + // Constructs a sequence of frames with a duration and a traversal style + inline FrameSequence(const float fFrameDuration = 0.1f, const Style nStyle = Style::Repeat) + { + m_fFrameDuration = fFrameDuration; + m_fFrameRate = 1.0f / m_fFrameDuration; + m_nStyle = nStyle; + } + + // Adds a frame to this sequence + inline void AddFrame(const Frame& frame) + { + m_vFrames.emplace_back(frame); + } + + // Returns a Frame Object for a given time into an animation + inline const Frame& GetFrame(const float fTime) const + { + return m_vFrames[ConvertTimeToFrame(fTime)]; + } + + private: + Style m_nStyle; + std::vector m_vFrames; + float m_fFrameDuration = 0.1f; + float m_fFrameRate = 10.0f; + + inline const size_t ConvertTimeToFrame(const float fTime) const + { + switch (m_nStyle) + { + case Style::Repeat: + return size_t(fTime * m_fFrameRate) % m_vFrames.size(); + break; + case Style::OneShot: + return std::clamp(size_t(fTime * m_fFrameRate), size_t(0), m_vFrames.size() - 1); + break; + case Style::PingPong: + // TODO + break; + case Style::Reverse: + return (m_vFrames.size() - 1) - (size_t(fTime * m_fFrameRate) % m_vFrames.size()); + break; + } + + return 0; + } + }; + + // A Animate2D::State is a lightweight token that can be attached to things + // that are animated. Under normal circumstances, it is never updated manually + struct AnimationState + { + private: + size_t nIndex = 0; + float fTime = 0.0f; + template + friend class Animation; + }; + + // Animation object holds a group of frame sequences and can mutate an AnimationState token + template + class Animation + { + public: + Animation() = default; + + // Change an animation state token to a new state + inline bool ChangeState(AnimationState& state, const StatesEnum& sStateName) const + { + size_t idx = m_mapStateIndices.at(sStateName); + if (state.nIndex != idx) + { + state.fTime = 0.0f; + state.nIndex = idx; + return true; + } + + return false; + } + + // Update an animation state token + inline void UpdateState(AnimationState& state, const float fElapsedTime) const + { + state.fTime += fElapsedTime; + } + + public: + // Retrieve the frame information for a given animation state + inline const Frame& GetFrame(const AnimationState& state) const + { + return m_vSequences[state.nIndex].GetFrame(state.fTime); + } + + public: + // Add a named Frame sequence as a state + inline void AddState(const StatesEnum& sStateName, const FrameSequence& sequence) + { + m_vSequences.emplace_back(sequence); + m_mapStateIndices[sStateName] = m_vSequences.size() - 1; + } + + private: + std::vector m_vSequences; + std::unordered_map m_mapStateIndices; + }; +} \ No newline at end of file diff --git a/utilities/olcUTIL_Geometry2D.h b/utilities/olcUTIL_Geometry2D.h index cd13a29..c625271 100644 --- a/utilities/olcUTIL_Geometry2D.h +++ b/utilities/olcUTIL_Geometry2D.h @@ -260,6 +260,12 @@ namespace olc::utils::geom2d }; + template + struct polygon + { + std::vector> vPoints; + }; + // ========================================================================================================================= // Closest(shape, point) =================================================================================================== @@ -274,25 +280,27 @@ namespace olc::utils::geom2d // Returns closest point on line to point template inline olc::v2d_generic closest(const line& l, const olc::v2d_generic& p) - { - // TODO: - return olc::v2d_generic(); + { + auto d = l.vector(); + double u = std::clamp(double(d.dot(p - l.start) / d.mag2()), 0.0, 1.0); + return l.start + d * u; } // Returns closest point on circle to point template inline olc::v2d_generic closest(const circle& c, const olc::v2d_generic& p) - { - // TODO: - return olc::v2d_generic(); + { + return c.pos + (p - c.pos).norm() * c.radius; } // Returns closest point on rectangle to point template inline olc::v2d_generic closest(const rect& r, const olc::v2d_generic& p) { - // TODO: - return olc::v2d_generic(); + // This could be a "constrain" function hmmmm + // TODO: Not quite what i wanted, should restrain to boundary + return olc::v2d_generic{ std::clamp(p.x, r.pos.x, r.pos.x + r.size.x), std::clamp(p.y, r.pos.y, r.pos.y + r.size.y) }; + } // Returns closest point on triangle to point