this repo has no description

fix: world generation using axial coord

Changed files
+115 -53
src
+19 -16
src/hex.zig
··· 6 7 pub const Point = struct { x: f32, y: f32 }; 8 9 - // Cubic coordinates: q + r + s = 0 constraint 10 - pub const Cubic = struct { 11 q: i32, 12 r: i32, 13 s: i32, 14 15 - pub fn init(q: i32, r: i32) Cubic { 16 return .{ .q = q, .r = r, .s = -q - r }; 17 } 18 19 - pub fn add(self: Cubic, other: Cubic) Cubic { 20 return .{ .q = self.q + other.q, .r = self.r + other.r, .s = self.s + other.s }; 21 } 22 23 - pub fn sub(self: Cubic, other: Cubic) Cubic { 24 return .{ .q = self.q - other.q, .r = self.r - other.r, .s = self.s - other.s }; 25 } 26 27 - pub fn neighbor(self: Cubic, dir: Direction) Cubic { 28 return self.add(direction_vectors[@intFromEnum(dir)]); 29 } 30 31 - pub fn toPixel(self: Cubic, size: f32) Point { 32 const q: f32 = @floatFromInt(self.q); 33 const r: f32 = @floatFromInt(self.r); 34 return .{ ··· 37 }; 38 } 39 40 - pub fn toOffset(self: Cubic) Offset { 41 const col = self.q; 42 - const row = self.r + @divFloor(self.q, 2); 43 return .{ .col = col, .row = row }; 44 } 45 }; ··· 48 col: i32, 49 row: i32, 50 51 - pub fn toCubic(self: Offset) Cubic { 52 const q = self.col; 53 - const r = self.row - @divFloor(self.col, 2); 54 - return Cubic.init(q, r); 55 } 56 }; 57 58 - const direction_vectors = [6]Cubic{ 59 .{ .q = 0, .r = -1, .s = 1 }, // top_left 60 .{ .q = 1, .r = -1, .s = 0 }, // top 61 .{ .q = 1, .r = 0, .s = -1 }, // top_right ··· 64 .{ .q = -1, .r = 0, .s = 1 }, // bottom_left 65 }; 66 67 - pub const HexCell = struct { inner_radius: f32, outer_radius: f32, height: f32, width: f32, center: Point, cubic: Cubic }; 68 pub const Cell = HexCell; 69 70 pub const HexDirections = enum { top_left, top, top_right, bottom_right, bottom, bottom_left }; ··· 88 return Point{ .x = center.x + x_offset, .y = center.y + y_offset }; 89 } 90 91 - pub fn new_hex(outer_radius: f32, cubic: Cubic) HexCell { 92 const size = outer_radius; 93 const inner = calculate_inner_radius(size); 94 const center = cubic.toPixel(size); ··· 98 pub fn new_hex_at(outer_radius: f32, center: Point) HexCell { 99 const size = outer_radius; 100 const inner = calculate_inner_radius(size); 101 - return HexCell{ .inner_radius = inner, .outer_radius = size, .height = sqrt_3 * size, .width = 2 * size, .center = center, .cubic = Cubic.init(0, 0) }; 102 } 103 104 pub fn add_hex(base: HexCell, direction: HexDirections) HexCell {
··· 6 7 pub const Point = struct { x: f32, y: f32 }; 8 9 + // Axial coordinates: q + r + s = 0 constraint 10 + // s = -q-r 11 + pub const Axial = struct { 12 q: i32, 13 r: i32, 14 s: i32, 15 16 + pub fn init(q: i32, r: i32) Axial { 17 return .{ .q = q, .r = r, .s = -q - r }; 18 } 19 20 + pub fn add(self: Axial, other: Axial) Axial { 21 return .{ .q = self.q + other.q, .r = self.r + other.r, .s = self.s + other.s }; 22 } 23 24 + pub fn sub(self: Axial, other: Axial) Axial { 25 return .{ .q = self.q - other.q, .r = self.r - other.r, .s = self.s - other.s }; 26 } 27 28 + pub fn neighbor(self: Axial, dir: Direction) Axial { 29 return self.add(direction_vectors[@intFromEnum(dir)]); 30 } 31 32 + pub fn toPixel(self: Axial, size: f32) Point { 33 const q: f32 = @floatFromInt(self.q); 34 const r: f32 = @floatFromInt(self.r); 35 return .{ ··· 38 }; 39 } 40 41 + // flat-top odd-q 42 + pub fn toOffset(self: Axial) Offset { 43 const col = self.q; 44 + const row = self.r + @divFloor(self.q - (self.q & 1), 2); 45 return .{ .col = col, .row = row }; 46 } 47 }; ··· 50 col: i32, 51 row: i32, 52 53 + // flat-top odd-q 54 + pub fn toAxial(self: Offset) Axial { 55 const q = self.col; 56 + const r = self.row - @divFloor(self.col - (self.col & 1), 2); 57 + return Axial.init(q, r); 58 } 59 }; 60 61 + const direction_vectors = [6]Axial{ 62 .{ .q = 0, .r = -1, .s = 1 }, // top_left 63 .{ .q = 1, .r = -1, .s = 0 }, // top 64 .{ .q = 1, .r = 0, .s = -1 }, // top_right ··· 67 .{ .q = -1, .r = 0, .s = 1 }, // bottom_left 68 }; 69 70 + pub const HexCell = struct { inner_radius: f32, outer_radius: f32, height: f32, width: f32, center: Point, cubic: Axial }; 71 pub const Cell = HexCell; 72 73 pub const HexDirections = enum { top_left, top, top_right, bottom_right, bottom, bottom_left }; ··· 91 return Point{ .x = center.x + x_offset, .y = center.y + y_offset }; 92 } 93 94 + pub fn new_hex(outer_radius: f32, cubic: Axial) HexCell { 95 const size = outer_radius; 96 const inner = calculate_inner_radius(size); 97 const center = cubic.toPixel(size); ··· 101 pub fn new_hex_at(outer_radius: f32, center: Point) HexCell { 102 const size = outer_radius; 103 const inner = calculate_inner_radius(size); 104 + return HexCell{ .inner_radius = inner, .outer_radius = size, .height = sqrt_3 * size, .width = 2 * size, .center = center, .cubic = Axial.init(0, 0) }; 105 } 106 107 pub fn add_hex(base: HexCell, direction: HexDirections) HexCell {
+55 -36
src/main.zig
··· 2 const lib = @import("zig_civ"); 3 const rl = @import("raylib"); 4 5 fn draw_hex(hex: lib.hex.HexCell) void { 6 var corners: [6]lib.hex.Point = undefined; 7 for (0..6) |i| { ··· 13 const start = corners[i]; 14 const end = corners[@mod(i + 1, 6)]; 15 16 - std.debug.print("sx: {d} sy: {d} ex:{d} ey:{d}\n", .{ start.x, start.y, end.x, end.y }); 17 - 18 - const start_x: i32 = @intFromFloat(@round(start.x)); 19 - const start_y: i32 = @intFromFloat(@round(start.y)); 20 - const end_x: i32 = @intFromFloat(@round(end.x)); 21 - const end_y: i32 = @intFromFloat(@round(end.y)); 22 23 rl.drawLine(start_x, start_y, end_x, end_y, .black); 24 } 25 } 26 27 - pub fn main() anyerror!void { 28 // Initialization 29 - //-------------------------------------------------------------------------------------- 30 const screenWidth = 800; 31 const screenHeight = 450; 32 const fps = 60; 33 34 rl.initWindow(screenWidth, screenHeight, "raylib.hex-zig [core] example - basic window"); 35 - defer rl.closeWindow(); // Close window and OpenGL context 36 37 - rl.setTargetFPS(fps); // Set our game to run at 60 frames-per-second 38 - //-------------------------------------------------------------------------------------- 39 40 // Main game loop 41 - while (!rl.windowShouldClose()) { // Detect window close button or ESC key 42 - // Update 43 - //---------------------------------------------------------------------------------- 44 - // TODO: Update your variables here 45 - //---------------------------------------------------------------------------------- 46 - const template = [5][8]u1{ 47 - .{ 1, 1, 1, 1, 1, 1, 1, 1 }, 48 - .{ 1, 1, 1, 0, 0, 1, 1, 1 }, 49 - .{ 1, 1, 0, 0, 0, 0, 1, 1 }, 50 - .{ 1, 1, 1, 0, 0, 1, 1, 1 }, 51 - .{ 1, 1, 1, 1, 1, 1, 1, 1 }, 52 - }; 53 - const grid = lib.world.generateWorld(5, 8, &template, 40.0); 54 - 55 - // Draw 56 - //---------------------------------------------------------------------------------- 57 rl.beginDrawing(); 58 defer rl.endDrawing(); 59 60 rl.clearBackground(.white); 61 62 - for (grid) |row| { 63 - for (row) |maybe_cell| { 64 - if (maybe_cell) |cell| { 65 - draw_hex(cell); 66 - } 67 - } 68 } 69 - 70 - //rl.drawText("Congrats! You created your first window!", 190, 200, 20, .light_gray); 71 - //---------------------------------------------------------------------------------- 72 } 73 }
··· 2 const lib = @import("zig_civ"); 3 const rl = @import("raylib"); 4 5 + const screen_offset_x: f32 = 400.0; // center of 800px screen 6 + const screen_offset_y: f32 = 225.0; // center of 450px screen 7 + 8 fn draw_hex(hex: lib.hex.HexCell) void { 9 var corners: [6]lib.hex.Point = undefined; 10 for (0..6) |i| { ··· 16 const start = corners[i]; 17 const end = corners[@mod(i + 1, 6)]; 18 19 + const start_x: i32 = @intFromFloat(@round(start.x + screen_offset_x)); 20 + const start_y: i32 = @intFromFloat(@round(start.y + screen_offset_y)); 21 + const end_x: i32 = @intFromFloat(@round(end.x + screen_offset_x)); 22 + const end_y: i32 = @intFromFloat(@round(end.y + screen_offset_y)); 23 24 rl.drawLine(start_x, start_y, end_x, end_y, .black); 25 } 26 } 27 28 + pub fn main() !void { 29 + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; 30 + defer _ = gpa.deinit(); 31 + const allocator = gpa.allocator(); 32 + 33 + // Axial template: 7x7 grid, center at [3][3], indexed [r+3][q+3] 34 + // Hexagon shape for radius 3 35 + //const template = [7][7]u1{ 36 + //// q: -3 -2 -1 0 1 2 3 37 + //.{ 0, 0, 0, 1, 1, 1, 1 }, // r = -3 38 + //.{ 0, 0, 1, 1, 1, 1, 1 }, // r = -2 39 + //.{ 0, 1, 1, 1, 1, 1, 1 }, // r = -1 40 + //.{ 1, 1, 1, 1, 1, 1, 1 }, // r = 0 41 + //.{ 1, 1, 1, 1, 1, 1, 0 }, // r = 1 42 + //.{ 1, 1, 1, 1, 1, 0, 0 }, // r = 2 43 + //.{ 1, 1, 1, 1, 0, 0, 0 }, // r = 3 44 + //}; 45 + 46 + //const template_rombus = [7][7]u1{ 47 + //.{ 1, 1, 1, 1, 1, 1, 1 }, 48 + //.{ 1, 1, 1, 1, 1, 1, 1 }, 49 + //.{ 1, 1, 1, 1, 1, 1, 1 }, 50 + //.{ 1, 1, 1, 1, 1, 1, 1 }, 51 + //.{ 1, 1, 1, 1, 1, 1, 1 }, 52 + //.{ 1, 1, 1, 1, 1, 1, 1 }, 53 + //.{ 1, 1, 1, 1, 1, 1, 1 }, 54 + //}; 55 + 56 + const template_left_triangle = [7][7]u1{ 57 + .{ 1, 1, 1, 1, 1, 1, 1 }, 58 + .{ 1, 1, 1, 1, 1, 1, 0 }, 59 + .{ 1, 1, 1, 1, 1, 0, 0 }, 60 + .{ 1, 1, 1, 1, 0, 0, 0 }, 61 + .{ 1, 1, 1, 0, 0, 0, 0 }, 62 + .{ 1, 1, 0, 0, 0, 0, 0 }, 63 + .{ 1, 0, 0, 0, 0, 0, 0 }, 64 + }; 65 + 66 + const template = template_left_triangle; 67 + 68 + var grid = try lib.world.generateFromTemplate(3, &template, 40.0, allocator); 69 + defer grid.deinit(allocator); 70 + 71 // Initialization 72 const screenWidth = 800; 73 const screenHeight = 450; 74 const fps = 60; 75 76 rl.initWindow(screenWidth, screenHeight, "raylib.hex-zig [core] example - basic window"); 77 + defer rl.closeWindow(); 78 79 + rl.setTargetFPS(fps); 80 81 // Main game loop 82 + while (!rl.windowShouldClose()) { 83 rl.beginDrawing(); 84 defer rl.endDrawing(); 85 86 rl.clearBackground(.white); 87 88 + for (grid.items) |cell| { 89 + draw_hex(cell); 90 } 91 } 92 }
+41 -1
src/world.zig
··· 2 const hex = @import("hex.zig"); 3 const tst = std.testing; 4 5 pub fn generateWorld(comptime H: usize, comptime W: usize, template: *const [H][W]u1, outer_radius: f32) [H][W]?hex.Cell { 6 var grid: [H][W]?hex.Cell = undefined; 7 ··· 9 for (0..W) |col| { 10 if (template[row][col] == 1) { 11 const offset = hex.Offset{ .col = @intCast(col), .row = @intCast(row) }; 12 - grid[row][col] = hex.new_hex(outer_radius, offset.toCubic()); 13 } else { 14 grid[row][col] = null; 15 }
··· 2 const hex = @import("hex.zig"); 3 const tst = std.testing; 4 5 + // Generate hexagon-shaped map with given radius (N rings around center) 6 + // Uses axial coords: |q|<=N, |r|<=N, |q+r|<=N 7 + pub fn generateHexWorld(comptime N: i32, outer_radius: f32, allocator: std.mem.Allocator) !std.ArrayListUnmanaged(hex.Cell) { 8 + var cells = std.ArrayListUnmanaged(hex.Cell){}; 9 + 10 + var q: i32 = -N; 11 + while (q <= N) : (q += 1) { 12 + const r1 = @max(-N, -q - N); 13 + const r2 = @min(N, -q + N); 14 + var r: i32 = r1; 15 + while (r <= r2) : (r += 1) { 16 + try cells.append(allocator, hex.new_hex(outer_radius, hex.Axial.init(q, r))); 17 + } 18 + } 19 + 20 + return cells; 21 + } 22 + 23 + // Template-based generation using axial coords 24 + // Template indexed by [r][q] where r,q range from -N to +N 25 + // Template size is (2*N+1) x (2*N+1), center is at [N][N] 26 + pub fn generateFromTemplate(comptime N: i32, template: *const [2 * N + 1][2 * N + 1]u1, outer_radius: f32, allocator: std.mem.Allocator) !std.ArrayListUnmanaged(hex.Cell) { 27 + var cells = std.ArrayListUnmanaged(hex.Cell){}; 28 + 29 + var q: i32 = -N; 30 + while (q <= N) : (q += 1) { 31 + var r: i32 = -N; 32 + while (r <= N) : (r += 1) { 33 + const ti_r: usize = @intCast(r + N); 34 + const ti_q: usize = @intCast(q + N); 35 + if (template[ti_r][ti_q] == 1) { 36 + try cells.append(allocator, hex.new_hex(outer_radius, hex.Axial.init(q, r))); 37 + } 38 + } 39 + } 40 + 41 + return cells; 42 + } 43 + 44 + // Old template-based generation (offset coords) 45 pub fn generateWorld(comptime H: usize, comptime W: usize, template: *const [H][W]u1, outer_radius: f32) [H][W]?hex.Cell { 46 var grid: [H][W]?hex.Cell = undefined; 47 ··· 49 for (0..W) |col| { 50 if (template[row][col] == 1) { 51 const offset = hex.Offset{ .col = @intCast(col), .row = @intCast(row) }; 52 + grid[row][col] = hex.new_hex(outer_radius, offset.toAxial()); 53 } else { 54 grid[row][col] = null; 55 }