196 lines
5.1 KiB
Zig
196 lines
5.1 KiB
Zig
const std = @import("std");
|
|
const rl = @import("raylib");
|
|
const remap_number = @import("./utils.zig").remap;
|
|
const assert = std.debug.assert;
|
|
|
|
pub fn Range(Number: type) type {
|
|
return struct {
|
|
const Self = @This();
|
|
|
|
lower: Number,
|
|
upper: Number,
|
|
|
|
pub fn init(lower: Number, upper: Number) Self {
|
|
return Self{
|
|
.lower = lower,
|
|
.upper = upper
|
|
};
|
|
}
|
|
|
|
pub fn initRect(rect: rl.Rectangle) [2]Self {
|
|
return .{
|
|
initRectX(rect),
|
|
initRectY(rect)
|
|
};
|
|
}
|
|
|
|
pub fn initRectX(rect: rl.Rectangle) Self {
|
|
return init(rect.x, rect.x + rect.width);
|
|
}
|
|
|
|
pub fn initRectY(rect: rl.Rectangle) Self {
|
|
return init(rect.y, rect.y + rect.height);
|
|
}
|
|
|
|
pub fn flip(self: Self) Self {
|
|
return init(self.upper, self.lower);
|
|
}
|
|
|
|
pub fn size(self: Self) Number {
|
|
return @abs(self.upper - self.lower);
|
|
}
|
|
|
|
pub fn hasExclusive(self: Self, number: Number) bool {
|
|
var upper = self.upper;
|
|
var lower = self.lower;
|
|
if (self.lower > self.upper) {
|
|
lower = self.upper;
|
|
upper = self.lower;
|
|
}
|
|
assert(lower <= upper);
|
|
|
|
return lower < number and number < upper;
|
|
|
|
}
|
|
|
|
pub fn hasInclusive(self: Self, number: Number) bool {
|
|
var upper = self.upper;
|
|
var lower = self.lower;
|
|
if (self.lower > self.upper) {
|
|
lower = self.upper;
|
|
upper = self.lower;
|
|
}
|
|
assert(lower <= upper);
|
|
|
|
return lower <= number and number <= upper;
|
|
}
|
|
|
|
pub fn remapTo(from: Self, to: Self, value: Number) Number {
|
|
return remap_number(Number, from.lower, from.upper, to.lower, to.upper, value);
|
|
}
|
|
|
|
pub fn add(self: Self, amount: Number) Self {
|
|
return init(
|
|
self.lower + amount,
|
|
self.upper + amount
|
|
);
|
|
}
|
|
|
|
pub fn sub(self: Self, amount: Number) Self {
|
|
return init(
|
|
self.lower - amount,
|
|
self.upper - amount
|
|
);
|
|
}
|
|
|
|
pub fn mul(self: Self, factor: Number) Self {
|
|
return init(
|
|
self.lower * factor,
|
|
self.upper * factor
|
|
);
|
|
}
|
|
|
|
pub fn zoom(self: Self, center: Number, factor: Number) Self {
|
|
return init(
|
|
(self.lower - center) * factor + center,
|
|
(self.upper - center) * factor + center
|
|
);
|
|
}
|
|
|
|
pub fn intersectPositive(self: Self, other: Self) Self {
|
|
// TODO: Figure out how would an intersection of "negative" ranges should look
|
|
// For now just coerce the negative ranges to positive ones.
|
|
const self_positive = self.toPositive();
|
|
const other_positive = other.toPositive();
|
|
|
|
return init(
|
|
@max(self_positive.lower, other_positive.lower),
|
|
@min(self_positive.upper, other_positive.upper)
|
|
);
|
|
}
|
|
|
|
pub fn toPositive(self: Self) Self {
|
|
if (self.isPositive()) {
|
|
return self;
|
|
} else {
|
|
return self.flip();
|
|
}
|
|
}
|
|
|
|
pub fn isPositive(self: Self) bool {
|
|
return self.upper >= self.lower;
|
|
}
|
|
|
|
pub fn isNegative(self: Self) bool {
|
|
return self.lower >= self.upper;
|
|
}
|
|
|
|
pub fn grow(self: Self, amount: Number) Self {
|
|
if (self.isPositive()) {
|
|
return init(
|
|
self.lower - amount,
|
|
self.upper + amount
|
|
);
|
|
} else {
|
|
return init(
|
|
self.lower + amount,
|
|
self.upper - amount
|
|
);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
pub const RangeF32 = Range(f32);
|
|
pub const RangeF64 = Range(f64);
|
|
|
|
test "math operations" {
|
|
try std.testing.expectEqual(
|
|
RangeF32.init(0, 10).mul(5),
|
|
RangeF32.init(0, 50)
|
|
);
|
|
|
|
try std.testing.expectEqual(
|
|
RangeF32.init(0, 10).add(5),
|
|
RangeF32.init(5, 15)
|
|
);
|
|
|
|
try std.testing.expectEqual(
|
|
RangeF32.init(0, 10).sub(5),
|
|
RangeF32.init(-5, 5)
|
|
);
|
|
}
|
|
|
|
test "size" {
|
|
try std.testing.expectEqual(
|
|
RangeF32.init(0, 10).size(),
|
|
10
|
|
);
|
|
|
|
try std.testing.expectEqual(
|
|
RangeF32.init(-10, 0).size(),
|
|
10
|
|
);
|
|
}
|
|
|
|
test "intersection" {
|
|
try std.testing.expectEqual(
|
|
RangeF32.init(0, 10).intersectPositive(RangeF32.init(5, 8)),
|
|
RangeF32.init(5, 8)
|
|
);
|
|
|
|
try std.testing.expectEqual(
|
|
RangeF32.init(0, 10).intersectPositive(RangeF32.init(-5, 8)),
|
|
RangeF32.init(0, 8)
|
|
);
|
|
|
|
try std.testing.expectEqual(
|
|
RangeF32.init(0, 10).intersectPositive(RangeF32.init(5, 15)),
|
|
RangeF32.init(5, 10)
|
|
);
|
|
|
|
try std.testing.expectEqual(
|
|
RangeF32.init(0, 10).intersectPositive(RangeF32.init(20, 30)),
|
|
RangeF32.init(20, 10)
|
|
);
|
|
} |