+19
-16
src/hex.zig
+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
+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
+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
}