From 6a2d132873ce7b40405a3dc000a12539fffd969b Mon Sep 17 00:00:00 2001 From: tslil clingman <> Date: Wed, 1 Sep 2021 12:59:50 -0400 Subject: Init --- src/sfml/graphics/rect.zig | 161 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 src/sfml/graphics/rect.zig (limited to 'src/sfml/graphics/rect.zig') diff --git a/src/sfml/graphics/rect.zig b/src/sfml/graphics/rect.zig new file mode 100644 index 0000000..184799c --- /dev/null +++ b/src/sfml/graphics/rect.zig @@ -0,0 +1,161 @@ +//! Utility class for manipulating 2D axis aligned rectangles. + +const sf = struct { + pub usingnamespace @import("../sfml.zig"); + pub usingnamespace system; +}; +const math = @import("std").math; + +pub fn Rect(comptime T: type) type { + return packed struct { + const Self = @This(); + + /// The CSFML vector type equivalent + const CsfmlEquivalent = switch (T) { + c_int => sf.c.sfIntRect, + f32 => sf.c.sfFloatRect, + else => void, + }; + + /// Creates a rect (just for convinience) + pub fn init(left: T, top: T, width: T, height: T) Self { + return Self{ + .left = left, + .top = top, + .width = width, + .height = height, + }; + } + + /// Makes a CSFML rect with this rect (only if the corresponding type exists) + /// This is mainly for the inner workings of this wrapper + pub fn toCSFML(self: Self) CsfmlEquivalent { + if (CsfmlEquivalent == void) @compileError("This rectangle type doesn't have a CSFML equivalent."); + return @bitCast(CsfmlEquivalent, self); + } + + /// Creates a rect from a CSFML one (only if the corresponding type exists) + /// This is mainly for the inner workings of this wrapper + pub fn fromCSFML(rect: CsfmlEquivalent) Self { + if (CsfmlEquivalent == void) @compileError("This rectangle type doesn't have a CSFML equivalent."); + return @bitCast(Self, rect); + } + + /// Checks if a point is inside this recangle + pub fn contains(self: Self, vec: sf.Vector2(T)) bool { + // Shamelessly stolen + var min_x: T = math.min(self.left, self.left + self.width); + var max_x: T = math.max(self.left, self.left + self.width); + var min_y: T = math.min(self.top, self.top + self.height); + var max_y: T = math.max(self.top, self.top + self.height); + + return (vec.x >= min_x and + vec.x < max_x and + vec.y >= min_y and + vec.y < max_y); + } + + /// Checks if two rectangles have a common intersection, if yes returns that zone, if not returns null + pub fn intersects(self: Self, other: Self) ?Self { + // Shamelessly stolen too + var r1_min_x: T = math.min(self.left, self.left + self.width); + var r1_max_x: T = math.max(self.left, self.left + self.width); + var r1_min_y: T = math.min(self.top, self.top + self.height); + var r1_max_y: T = math.max(self.top, self.top + self.height); + + var r2_min_x: T = math.min(other.left, other.left + other.width); + var r2_max_x: T = math.max(other.left, other.left + other.width); + var r2_min_y: T = math.min(other.top, other.top + other.height); + var r2_max_y: T = math.max(other.top, other.top + other.height); + + var inter_left: T = math.max(r1_min_x, r2_min_x); + var inter_top: T = math.max(r1_min_y, r2_min_y); + var inter_right: T = math.min(r1_max_x, r2_max_x); + var inter_bottom: T = math.min(r1_max_y, r2_max_y); + + if (inter_left < inter_right and inter_top < inter_bottom) { + return Self.init(inter_left, inter_top, inter_right - inter_left, inter_bottom - inter_top); + } else { + return null; + } + } + + /// Checks if two rectangles are the same + pub fn equals(self: Self, other: Self) bool { + return (self.left == other.left and + self.top == other.top and + self.width == other.width and + self.height == other.height); + } + + /// Gets a vector with left and top components inside + pub fn getCorner(self: Self) sf.Vector2(T) { + return sf.Vector2(T){ .x = self.left, .y = self.top }; + } + /// Gets a vector with the bottom right corner coordinates + pub fn getOtherCorner(self: Self) sf.Vector2(T) { + return self.getCorner().add(self.getSize()); + } + /// Gets a vector with width and height components inside + pub fn getSize(self: Self) sf.Vector2(T) { + return sf.Vector2(T){ .x = self.width, .y = self.height }; + } + + /// x component of the top left corner + left: T, + /// x component of the top left corner + top: T, + /// width of the rectangle + width: T, + /// height of the rectangle + height: T + }; +} + +test "rect: intersect" { + const tst = @import("std").testing; + + var r1 = Rect(c_int).init(0, 0, 10, 10); + var r2 = Rect(c_int).init(6, 6, 20, 20); + var r3 = Rect(c_int).init(-5, -5, 10, 10); + + try tst.expectEqual(@as(?Rect(c_int), null), r2.intersects(r3)); + + var inter1: sf.c.sfIntRect = undefined; + var inter2: sf.c.sfIntRect = undefined; + + try tst.expectEqual(sf.c.sfIntRect_intersects(&r1.toCSFML(), &r2.toCSFML(), &inter1), 1); + try tst.expectEqual(sf.c.sfIntRect_intersects(&r1.toCSFML(), &r3.toCSFML(), &inter2), 1); + + try tst.expectEqual(Rect(c_int).fromCSFML(inter1), r1.intersects(r2).?); + try tst.expectEqual(Rect(c_int).fromCSFML(inter2), r1.intersects(r3).?); +} + +test "rect: contains" { + const tst = @import("std").testing; + + var r1 = Rect(f32).init(0, 0, 10, 10); + + try tst.expect(r1.contains(.{ .x = 0, .y = 0 })); + try tst.expect(r1.contains(.{ .x = 9, .y = 9 })); + try tst.expect(!r1.contains(.{ .x = 5, .y = -1 })); + try tst.expect(!r1.contains(.{ .x = 10, .y = 5 })); +} + +test "rect: sane from/to CSFML rect" { + const tst = @import("std").testing; + + inline for ([_]type{ c_int, f32 }) |T| { + const rect = Rect(T).init(1, 3, 5, 10); + const crect = rect.toCSFML(); + + try tst.expectEqual(rect.left, crect.left); + try tst.expectEqual(rect.top, crect.top); + try tst.expectEqual(rect.width, crect.width); + try tst.expectEqual(rect.height, crect.height); + + const rect2 = Rect(T).fromCSFML(crect); + + try tst.expectEqual(rect, rect2); + } +} -- cgit v1.3.1