From 6742491be69f0c932d7ffa8e25dcdb8289118016 Mon Sep 17 00:00:00 2001 From: Janis Date: Sun, 18 Dec 2022 20:46:00 +0100 Subject: [PATCH] extracted box stuff to box.zig --- box.zig | 366 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ main.zig | 200 +----------------------------- 2 files changed, 372 insertions(+), 194 deletions(-) create mode 100644 box.zig diff --git a/box.zig b/box.zig new file mode 100644 index 0000000..912ef34 --- /dev/null +++ b/box.zig @@ -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)); +} diff --git a/main.zig b/main.zig index 48b4f6d..cbedab8 100644 --- a/main.zig +++ b/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();