extracted box stuff to box.zig
This commit is contained in:
parent
dd609d6d36
commit
6742491be6
366
box.zig
Normal file
366
box.zig
Normal file
|
@ -0,0 +1,366 @@
|
|||
///! Box math, including Boxes, Point, Size and Selection
|
||||
const std = @import("std");
|
||||
|
||||
pub const Point = struct {
|
||||
const Self = @This();
|
||||
|
||||
x: i32 = 0,
|
||||
y: i32 = 0,
|
||||
|
||||
pub fn length(self: Self) f32 {
|
||||
return @sqrt(@intToFloat(f32, self.x*self.x + self.y*self.y));
|
||||
}
|
||||
|
||||
pub fn distanceTo(self: Self, other: Self) f32 {
|
||||
const delta = self.added(other.inverted());
|
||||
|
||||
return delta.length();
|
||||
}
|
||||
|
||||
pub fn inverted(self: *const Self) Self {
|
||||
return Self{
|
||||
.x = self.x * -1,
|
||||
.y = self.y * -1,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn add(self: *Self, other: Self) void {
|
||||
self.x += other.x;
|
||||
self.y += other.y;
|
||||
}
|
||||
|
||||
pub fn added(self: *const Self, other: Self) Self {
|
||||
var new = self.*;
|
||||
new.add(other);
|
||||
return new;
|
||||
}
|
||||
|
||||
|
||||
pub fn eq(self: Self, other: Self) bool {
|
||||
return self.x == other.x and self.y == other.y;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Size = struct {
|
||||
const Self = @This();
|
||||
width: u32 = 0,
|
||||
height: u32 = 0,
|
||||
|
||||
pub fn size(self: *Self) usize {
|
||||
return self.width * self.height;
|
||||
}
|
||||
|
||||
pub fn eq(self: Self, other: Self) bool {
|
||||
return self.width == other.width and self.height == other.height;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Selection = struct {
|
||||
const Self = @This();
|
||||
|
||||
start: Point,
|
||||
end: Point,
|
||||
|
||||
pub fn fromPoint(point: Point) Self {
|
||||
return .{
|
||||
.start = point,
|
||||
.end = point,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn asBox(self: Self) Box {
|
||||
return Box.fromCorners(self.start, self.end);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Box = struct {
|
||||
const Self = @This();
|
||||
|
||||
position: Point = .{.x = 0, .y = 0,},
|
||||
extents: Size = .{.width = 0, .height = 0,},
|
||||
|
||||
pub fn default() Self {
|
||||
return Self {
|
||||
.position = .{.x = 0, .y = 0,},
|
||||
.extents = .{.width = 0, .height = 0,},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
pub fn eq(self: Self, other: Self) bool {
|
||||
return self.position.eq(other.position) and self.extents.eq(other.extents);
|
||||
}
|
||||
|
||||
pub fn intersects(self: *const Self, other: *const Self) bool {
|
||||
const x1 = self.position.x;
|
||||
const y1 = self.position.y;
|
||||
const w1 = @intCast(isize, self.extents.width);
|
||||
const h1 = @intCast(isize, self.extents.height);
|
||||
|
||||
const x2 = other.position.x;
|
||||
const y2 = other.position.y;
|
||||
|
||||
const w2 = @intCast(isize, other.extents.width);
|
||||
const h2 = @intCast(isize, other.extents.height);
|
||||
|
||||
// return self.x < other.x + other.width and
|
||||
// self.x + self.width > other.x and
|
||||
// self.y < other.y + other.height and
|
||||
// self.height + self.y > other.y;
|
||||
|
||||
return x1 < x2 + w2 and
|
||||
x1 + w1 > x2 and
|
||||
y1 < y1 + h2 and
|
||||
y1 + h1 > y2;
|
||||
}
|
||||
|
||||
pub fn fromCorners(a: Point, b: Point) Self {
|
||||
const top = @min(a.y, b.y);
|
||||
const bot = @max(a.y, b.y);
|
||||
const left = @min(a.x, b.x);
|
||||
const right = @max(a.x, b.x);
|
||||
|
||||
const pos = Point{.x = left, .y = top,};
|
||||
const extents = Size{
|
||||
.width = @intCast(u32, right - left),
|
||||
.height = @intCast(u32, bot - top),
|
||||
};
|
||||
|
||||
return Self {
|
||||
.position = pos,
|
||||
.extents = extents,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getInner(comptime T: anytype, v: anytype) T {
|
||||
switch (@typeInfo(T)) {
|
||||
.Float => {
|
||||
return @intToFloat(T, v);
|
||||
},
|
||||
.Int => {
|
||||
return @intCast(T, v);
|
||||
},
|
||||
else => {
|
||||
@compileError("can only cast positional scalar to integer or floating point types");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getCenter(self: *const Self) Point {
|
||||
return .{
|
||||
.x = self.position.x + @intCast(i32, self.extents.width / 2),
|
||||
.y = self.position.y + @intCast(i32, self.extents.height / 2),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getX(self: *const Self, comptime T: anytype) T {
|
||||
return getInner(T, self.position.x);
|
||||
}
|
||||
|
||||
pub fn getY(self: *const Self, comptime T: anytype) T {
|
||||
return getInner(T, self.position.y);
|
||||
}
|
||||
|
||||
pub fn getWidth(self: *const Self, comptime T: anytype) T {
|
||||
return getInner(T, self.extents.width);
|
||||
}
|
||||
|
||||
pub fn getHeight(self: *const Self, comptime T: anytype) T {
|
||||
return getInner(T, self.extents.height);
|
||||
}
|
||||
|
||||
pub fn translate(self: *Self, delta: Point) void {
|
||||
self.position.add(delta);
|
||||
}
|
||||
|
||||
pub fn translated(self: Self, delta: Point) Self {
|
||||
var new = self;
|
||||
new.translate(delta);
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
pub fn contains(self: *const Self, point: Point) bool {
|
||||
const x1 = self.position.x;
|
||||
const y1 = self.position.y;
|
||||
const w1 = @intCast(isize, self.extents.width);
|
||||
const h1 = @intCast(isize, self.extents.height);
|
||||
|
||||
const x2 = point.x;
|
||||
const y2 = point.y;
|
||||
|
||||
// return self.x <= point.x and self.x + self.width > point.x and
|
||||
// self.y <= point.y and self.y + self.height > point.y;
|
||||
|
||||
return x1 <= x2 and x1 + w1 > x2 and
|
||||
y1 <= y2 and y1 + h1 > y2;
|
||||
}
|
||||
|
||||
pub fn size(self: *Self) usize {
|
||||
return self.extents.size();
|
||||
}
|
||||
};
|
||||
|
||||
pub const Boxes = struct {
|
||||
const Self = @This();
|
||||
|
||||
boxes: std.ArrayList(Box),
|
||||
|
||||
const Iterator = struct {
|
||||
items: []Box,
|
||||
i: usize = 0,
|
||||
|
||||
pub fn next(it: *@This()) ?*Box {
|
||||
if (it.i < it.items.len) {
|
||||
const item = &it.items[it.i];
|
||||
it.i += 1;
|
||||
|
||||
return item;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const Containing = struct {
|
||||
iter: Iterator,
|
||||
point: Point,
|
||||
|
||||
pub fn next(it: *@This()) ?*Box {
|
||||
while (it.iter.next()) |item| {
|
||||
if (item.contains(it.point)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn init(alloc: std.mem.Allocator) Self {
|
||||
const boxes = std.ArrayList(Box).init(alloc);
|
||||
return Self {
|
||||
.boxes = boxes,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.boxes.deinit();
|
||||
}
|
||||
|
||||
pub fn iter(self: *Self) Iterator {
|
||||
return .{
|
||||
.items = self.boxes.items,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn iterBoxesContaining(self: *Self, point: Point) Containing {
|
||||
return .{
|
||||
.iter = self.iter(),
|
||||
.point = point,
|
||||
};
|
||||
}
|
||||
|
||||
fn findClosestItemTo(it_: anytype, point: Point) ?*Box {
|
||||
var closest: ?*Box = null;
|
||||
var closest_distance: ?f32 = null;
|
||||
|
||||
var it = it_;
|
||||
while (it.next()) |box| {
|
||||
const distance = box.getCenter().distanceTo(point);
|
||||
if (closest_distance == null or distance < closest_distance.? or
|
||||
(distance == closest_distance.? and closest.?.extents.size() > box.extents.size())
|
||||
) {
|
||||
closest_distance = distance;
|
||||
closest = box;
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
pub fn findClosestBoxTo(self: *Self, point: Point) ?*Box {
|
||||
return findClosestItemTo(self.iter(), point);
|
||||
}
|
||||
|
||||
pub fn findClosestBoxToAndContaining(self: *Self, point: Point) ?*Box {
|
||||
return findClosestItemTo(self.iterBoxesContaining(point), point);
|
||||
}
|
||||
};
|
||||
|
||||
test "boxes" {
|
||||
std.testing.refAllDecls(Boxes);
|
||||
|
||||
const a1 = Box{.extents = .{.width = 10, .height = 10,}};
|
||||
const a2 = Box{.extents = .{.width = 20, .height = 20,}};
|
||||
|
||||
const a3 = Box{
|
||||
.position = .{.x = 0, .y = 0,},
|
||||
.extents = .{.width = 20, .height = 20,},
|
||||
};
|
||||
|
||||
const a4 = Box{
|
||||
.position = .{.x = 5, .y = 5,},
|
||||
.extents = .{.width = 10, .height = 10,},
|
||||
};
|
||||
|
||||
var boxes = Boxes.init(std.testing.allocator);
|
||||
defer boxes.deinit();
|
||||
|
||||
try boxes.boxes.append(a1);
|
||||
try boxes.boxes.append(a2);
|
||||
|
||||
{
|
||||
std.debug.print("iter boxes..\n", .{});
|
||||
|
||||
var it = boxes.iter();
|
||||
while (it.next()) |next| {
|
||||
std.debug.print("item: {?}\n", .{next});
|
||||
}
|
||||
}
|
||||
|
||||
const point = Point{.x = 15, .y = 15};
|
||||
|
||||
{
|
||||
std.debug.print("iter boxes containing {?}..\n", .{point});
|
||||
|
||||
var it = boxes.iterBoxesContaining(point);
|
||||
while (it.next()) |next| {
|
||||
std.debug.print("item: {?}\n", .{next});
|
||||
}
|
||||
}
|
||||
|
||||
var it = boxes.iterBoxesContaining(.{.x = 18, .y = 15});
|
||||
std.debug.assert(a2.contains(.{.x = 18, .y = 15}));
|
||||
std.debug.assert(
|
||||
it.next().?.eq(a2));
|
||||
|
||||
try boxes.boxes.append(a3);
|
||||
try boxes.boxes.append(a4);
|
||||
}
|
||||
|
||||
test "box" {
|
||||
const a = Box.default();
|
||||
const a2 = Box{.extents = .{.width = 1, .height = 1,}};
|
||||
const b = Box.default();
|
||||
const b2 = Box{.extents = .{.width = 1, .height = 1,}};
|
||||
|
||||
std.debug.assert(!a.contains(.{.x = 0, .y = 0}));
|
||||
std.debug.assert(!a.intersects(&b));
|
||||
|
||||
std.debug.assert(a2.contains(.{.x = 0, .y = 0}));
|
||||
std.debug.assert(a2.intersects(&b2));
|
||||
|
||||
const c1 = Box{
|
||||
.position = .{.x = 1, .y = 1,},
|
||||
.extents = .{.width = 2, .height = 2,},
|
||||
};
|
||||
|
||||
const c2 = Box{
|
||||
.position = .{.x = 1, .y = 1,},
|
||||
.extents = .{.width = 1, .height = 1,},
|
||||
};
|
||||
|
||||
std.debug.assert(!c1.intersects(&a2));
|
||||
std.debug.assert(c1.intersects(&c2));
|
||||
}
|
200
main.zig
200
main.zig
|
@ -7,6 +7,12 @@ const zxdg = wayland.client.zxdg;
|
|||
const zwlr = wayland.client.zwlr;
|
||||
const list = wayland.server.wl.list;
|
||||
|
||||
const Utils = @import("box.zig");
|
||||
const Point = Utils.Point;
|
||||
const Box = Utils.Box;
|
||||
const Size = Utils.Size;
|
||||
const Selection = Utils.Selection;
|
||||
|
||||
const xkb = @cImport({
|
||||
@cInclude("xkbcommon/xkbcommon.h");
|
||||
});
|
||||
|
@ -15,58 +21,6 @@ const Cairo = @cImport({
|
|||
@cInclude("cairo/cairo.h");
|
||||
});
|
||||
|
||||
const Point = struct {
|
||||
const Self = @This();
|
||||
|
||||
x: i32 = 0,
|
||||
y: i32 = 0,
|
||||
|
||||
fn inverted(self: *const Self) Self {
|
||||
return Self{
|
||||
.x = self.x * -1,
|
||||
.y = self.y * -1,
|
||||
};
|
||||
}
|
||||
|
||||
fn add(self: *Self, other: Self) void {
|
||||
self.x += other.x;
|
||||
self.y += other.y;
|
||||
}
|
||||
|
||||
fn added(self: *const Self, other: Self) Self {
|
||||
var new = self.*;
|
||||
new.add(other);
|
||||
return new;
|
||||
}
|
||||
};
|
||||
|
||||
const Size = struct {
|
||||
const Self = @This();
|
||||
width: u32 = 0,
|
||||
height: u32 = 0,
|
||||
|
||||
fn size(self: *Self) usize {
|
||||
return self.width * self.height;
|
||||
}
|
||||
};
|
||||
|
||||
const Selection = struct {
|
||||
const Self = @This();
|
||||
|
||||
start: Point,
|
||||
end: Point,
|
||||
|
||||
fn fromPoint(point: Point) Self {
|
||||
return .{
|
||||
.start = point,
|
||||
.end = point,
|
||||
};
|
||||
}
|
||||
|
||||
fn asBox(self: Self) Box {
|
||||
return Box.fromCorners(self.start, self.end);
|
||||
}
|
||||
};
|
||||
|
||||
const Buffer = struct {
|
||||
const Self = @This();
|
||||
|
@ -214,148 +168,6 @@ test "buffers" {
|
|||
std.testing.refAllDecls(Buffers);
|
||||
}
|
||||
|
||||
const Box = struct {
|
||||
const Self = @This();
|
||||
|
||||
position: Point = .{.x = 0, .y = 0,},
|
||||
extents: Size = .{.width = 0, .height = 0,},
|
||||
|
||||
fn default() Self {
|
||||
return Self {
|
||||
.position = .{.x = 0, .y = 0,},
|
||||
.extents = .{.width = 0, .height = 0,},
|
||||
};
|
||||
}
|
||||
|
||||
fn intersects(self: *const Self, other: *const Self) bool {
|
||||
const x1 = self.position.x;
|
||||
const y1 = self.position.y;
|
||||
const w1 = @intCast(isize, self.extents.width);
|
||||
const h1 = @intCast(isize, self.extents.height);
|
||||
|
||||
const x2 = other.position.x;
|
||||
const y2 = other.position.y;
|
||||
|
||||
const w2 = @intCast(isize, other.extents.width);
|
||||
const h2 = @intCast(isize, other.extents.height);
|
||||
|
||||
// return self.x < other.x + other.width and
|
||||
// self.x + self.width > other.x and
|
||||
// self.y < other.y + other.height and
|
||||
// self.height + self.y > other.y;
|
||||
|
||||
return x1 < x2 + w2 and
|
||||
x1 + w1 > x2 and
|
||||
y1 < y1 + h2 and
|
||||
y1 + h1 > y2;
|
||||
}
|
||||
|
||||
fn fromCorners(a: Point, b: Point) Self {
|
||||
const top = @min(a.y, b.y);
|
||||
const bot = @max(a.y, b.y);
|
||||
const left = @min(a.x, b.x);
|
||||
const right = @max(a.x, b.x);
|
||||
|
||||
const pos = Point{.x = left, .y = top,};
|
||||
const extents = Size{
|
||||
.width = @intCast(u32, right - left),
|
||||
.height = @intCast(u32, bot - top),
|
||||
};
|
||||
|
||||
return Self {
|
||||
.position = pos,
|
||||
.extents = extents,
|
||||
};
|
||||
}
|
||||
|
||||
fn getInner(comptime T: anytype, v: anytype) T {
|
||||
switch (@typeInfo(T)) {
|
||||
.Float => {
|
||||
return @intToFloat(T, v);
|
||||
},
|
||||
.Int => {
|
||||
return @intCast(T, v);
|
||||
},
|
||||
else => {
|
||||
@compileError("can only cast positional scalar to integer or floating point types");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn getX(self: *const Self, comptime T: anytype) T {
|
||||
return getInner(T, self.position.x);
|
||||
}
|
||||
|
||||
fn getY(self: *const Self, comptime T: anytype) T {
|
||||
return getInner(T, self.position.y);
|
||||
}
|
||||
|
||||
fn getWidth(self: *const Self, comptime T: anytype) T {
|
||||
return getInner(T, self.extents.width);
|
||||
}
|
||||
|
||||
fn getHeight(self: *const Self, comptime T: anytype) T {
|
||||
return getInner(T, self.extents.height);
|
||||
}
|
||||
|
||||
fn translate(self: *Self, delta: Point) void {
|
||||
self.position.add(delta);
|
||||
}
|
||||
|
||||
fn translated(self: Self, delta: Point) Self {
|
||||
var new = self;
|
||||
new.translate(delta);
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
fn contains(self: *const Self, point: Point) bool {
|
||||
const x1 = self.position.x;
|
||||
const y1 = self.position.y;
|
||||
const w1 = @intCast(isize, self.extents.width);
|
||||
const h1 = @intCast(isize, self.extents.height);
|
||||
|
||||
const x2 = point.x;
|
||||
const y2 = point.y;
|
||||
|
||||
// return self.x <= point.x and self.x + self.width > point.x and
|
||||
// self.y <= point.y and self.y + self.height > point.y;
|
||||
|
||||
return x1 <= x2 and x1 + w1 > x2 and
|
||||
y1 <= y2 and y1 + h1 > y2;
|
||||
}
|
||||
|
||||
fn size(self: *Self) usize {
|
||||
return self.extents.size();
|
||||
}
|
||||
};
|
||||
|
||||
test "box" {
|
||||
const a = Box.default();
|
||||
const a2 = Box{.extents = .{.width = 1, .height = 1,}};
|
||||
const b = Box.default();
|
||||
const b2 = Box{.extents = .{.width = 1, .height = 1,}};
|
||||
|
||||
std.debug.assert(!a.contains(.{.x = 0, .y = 0}));
|
||||
std.debug.assert(!a.intersects(&b));
|
||||
|
||||
std.debug.assert(a2.contains(.{.x = 0, .y = 0}));
|
||||
std.debug.assert(a2.intersects(&b2));
|
||||
|
||||
const c1 = Box{
|
||||
.position = .{.x = 1, .y = 1,},
|
||||
.extents = .{.width = 2, .height = 2,},
|
||||
};
|
||||
|
||||
const c2 = Box{
|
||||
.position = .{.x = 1, .y = 1,},
|
||||
.extents = .{.width = 1, .height = 1,},
|
||||
};
|
||||
|
||||
std.debug.assert(!c1.intersects(&a2));
|
||||
std.debug.assert(c1.intersects(&c2));
|
||||
}
|
||||
|
||||
fn ListItem(comptime T: type) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
|
|
Loading…
Reference in a new issue