//! By convention, root.zig is the root source file when making a library. const std = @import("std"); const PI: comptime_float = std.math.pi; const sqrt_3: comptime_float = std.math.sqrt(3.0); pub const Point = struct { x: f32, y: f32 }; // Axial coordinates: q + r + s = 0 constraint // s = -q-r pub const Axial = struct { q: i32, r: i32, s: i32, pub fn init(q: i32, r: i32) Axial { return .{ .q = q, .r = r, .s = -q - r }; } pub fn add(self: Axial, other: Axial) Axial { return .{ .q = self.q + other.q, .r = self.r + other.r, .s = self.s + other.s }; } pub fn sub(self: Axial, other: Axial) Axial { return .{ .q = self.q - other.q, .r = self.r - other.r, .s = self.s - other.s }; } pub fn neighbor(self: Axial, dir: Direction) Axial { return self.add(direction_vectors[@intFromEnum(dir)]); } pub fn toPixel(self: Axial, size: f32) Point { const q: f32 = @floatFromInt(self.q); const r: f32 = @floatFromInt(self.r); return .{ .x = size * (1.5 * q), .y = size * (sqrt_3 * (r + q / 2.0)), }; } // flat-top odd-q pub fn toOffset(self: Axial) Offset { const col = self.q; const row = self.r + @divFloor(self.q - (self.q & 1), 2); return .{ .col = col, .row = row }; } }; pub const Offset = struct { col: i32, row: i32, // flat-top odd-q pub fn toAxial(self: Offset) Axial { const q = self.col; const r = self.row - @divFloor(self.col - (self.col & 1), 2); return Axial.init(q, r); } }; const direction_vectors = [6]Axial{ .{ .q = 0, .r = -1, .s = 1 }, // top_left .{ .q = 1, .r = -1, .s = 0 }, // top .{ .q = 1, .r = 0, .s = -1 }, // top_right .{ .q = 0, .r = 1, .s = -1 }, // bottom_right .{ .q = -1, .r = 1, .s = 0 }, // bottom .{ .q = -1, .r = 0, .s = 1 }, // bottom_left }; pub const HexCell = struct { inner_radius: f32, outer_radius: f32, height: f32, width: f32, center: Point, cubic: Axial }; pub const Cell = HexCell; pub const HexDirections = enum { top_left, top, top_right, bottom_right, bottom, bottom_left }; pub const Direction = HexDirections; fn calculate_outer_radius(inner_radius: f32) f32 { return 2.0 * inner_radius / sqrt_3; } fn calculate_inner_radius(outer_radius: f32) f32 { return outer_radius / 2.0 * sqrt_3; } pub fn calculate_hex_corners(center: Point, size: f32, _i: usize) Point { const i: f32 = @floatFromInt(_i); const angle_deg = 60.0 * i; const angle_rad = std.math.degreesToRadians(angle_deg); const x_offset: f32 = size * std.math.cos(angle_rad); const y_offset: f32 = size * std.math.sin(angle_rad); return Point{ .x = center.x + x_offset, .y = center.y + y_offset }; } pub fn new_hex(outer_radius: f32, cubic: Axial) HexCell { const size = outer_radius; const inner = calculate_inner_radius(size); const center = cubic.toPixel(size); return HexCell{ .inner_radius = inner, .outer_radius = size, .height = sqrt_3 * size, .width = 2 * size, .center = center, .cubic = cubic }; } pub fn new_hex_at(outer_radius: f32, center: Point) HexCell { const size = outer_radius; const inner = calculate_inner_radius(size); return HexCell{ .inner_radius = inner, .outer_radius = size, .height = sqrt_3 * size, .width = 2 * size, .center = center, .cubic = Axial.init(0, 0) }; } pub fn add_hex(base: HexCell, direction: HexDirections) HexCell { const new_cubic = base.cubic.neighbor(direction); return new_hex(base.outer_radius, new_cubic); }