creates new project
This commit is contained in:
		
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| /.zig-cache/ | ||||
| /zig-out/ | ||||
							
								
								
									
										62
									
								
								build.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								build.zig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| const std = @import("std"); | ||||
|  | ||||
| pub fn build(b: *std.Build) void { | ||||
|     const target   = b.standardTargetOptions(.{}); | ||||
|     const optimize = b.standardOptimizeOption(.{}); | ||||
|  | ||||
|     // ---- deps | ||||
|     const dvui_dep = b.dependency("dvui", .{ | ||||
|         .target = target, | ||||
|         .optimize = optimize, | ||||
|         .backend = .sdl, | ||||
|         .sdl3 = true, | ||||
|     }); | ||||
|  | ||||
|     // ---- library | ||||
|     const lib_mod = b.createModule(.{ | ||||
|         .root_source_file = b.path("src/hncore/root.zig"), | ||||
|         .target   = target, | ||||
|         .optimize = optimize, | ||||
|     }); | ||||
|  | ||||
|     const lib = b.addLibrary(.{ | ||||
|         .linkage = .static, | ||||
|         .name = "hncore", | ||||
|         .root_module = lib_mod, | ||||
|     }); | ||||
|     b.installArtifact(lib); | ||||
|  | ||||
|  | ||||
|     // ---- executable | ||||
|     const exe_mod = b.createModule(.{ | ||||
|         .root_source_file = b.path("src/hnet/main.zig"), | ||||
|         .target   = target, | ||||
|         .optimize = optimize, | ||||
|     }); | ||||
|     exe_mod.addImport("hncore", lib_mod); | ||||
|     exe_mod.addImport("dvui", dvui_dep.module("dvui_sdl")); | ||||
|  | ||||
|     const exe = b.addExecutable(.{ | ||||
|         .name = "hnet", | ||||
|         .root_module = exe_mod, | ||||
|     }); | ||||
|     b.installArtifact(exe); | ||||
|  | ||||
|     // ---- running | ||||
|     const run_cmd = b.addRunArtifact(exe); | ||||
|     run_cmd.step.dependOn(b.getInstallStep()); | ||||
|     if (b.args) |args| { | ||||
|         run_cmd.addArgs(args); | ||||
|     } | ||||
|     const run_step = b.step("run", "Run the app"); | ||||
|     run_step.dependOn(&run_cmd.step); | ||||
|  | ||||
|     // ---- testing | ||||
|     const lib_unit_tests = b.addTest(.{ | ||||
|         .root_module = lib_mod, | ||||
|     }); | ||||
|     const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); | ||||
|  | ||||
|     const test_step = b.step("test", "Run unit tests"); | ||||
|     test_step.dependOn(&run_lib_unit_tests.step); | ||||
| } | ||||
							
								
								
									
										19
									
								
								build.zig.zon
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								build.zig.zon
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| .{ | ||||
|     .name = .heavens_net, | ||||
|     .version = "0.0.1", | ||||
|     .fingerprint = 0x5cbe403baf740fcb, | ||||
|     .minimum_zig_version = "0.15.0-dev.149+2b57f6b71", | ||||
|  | ||||
|     .dependencies = .{ | ||||
|         .dvui = .{ | ||||
|             .url  = "https://github.com/david-vanderson/dvui/archive/9f446c8600b3385418c8926481076c335261f222.zip", | ||||
|             .hash = "dvui-0.2.0-AQFJmesqywBKCM6r9orR324EGhZvGsjhJw0ZHWcvwzyh", | ||||
|         }, | ||||
|     }, | ||||
|  | ||||
|     .paths = .{ | ||||
|         "build.zig", | ||||
|         "build.zig.zon", | ||||
|         "src", | ||||
|     }, | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								src/hncore/.DS_Store
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/hncore/.DS_Store
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										249
									
								
								src/hncore/Digraph.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								src/hncore/Digraph.zig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,249 @@ | ||||
| const std = @import("std"); | ||||
|  | ||||
| /// A data type to store relations of nodes in directional-graph. | ||||
| pub fn Digraph(comptime T: type) type { | ||||
|     return struct { | ||||
|         const Node     = T; | ||||
|         const Conn     = struct { from: T, to: T, }; | ||||
|         const ConnList = std.ArrayList(Conn); | ||||
|         const Error    = error { | ||||
|             AlreadyConnected, | ||||
|             NotConnected, | ||||
|         }; | ||||
|  | ||||
|         map: ConnList, | ||||
|  | ||||
|         /// | ||||
|         pub fn init(alloc: std.mem.Allocator, map_unsorted: []const Conn) !@This() { | ||||
|             var map_sorted = ConnList.init(alloc); | ||||
|             try map_sorted.ensureTotalCapacity(map_unsorted.len); | ||||
|             for (map_unsorted) |conn| { | ||||
|                 try map_sorted.append(conn); | ||||
|             } | ||||
|             std.mem.sort(Conn, map_sorted.items, {}, compare_conn); | ||||
|  | ||||
|             return .{ | ||||
|                 .map  = map_sorted, | ||||
|             }; | ||||
|         } | ||||
|         /// | ||||
|         pub fn deinit(self: *@This()) void { | ||||
|             self.map.deinit(); | ||||
|         } | ||||
|  | ||||
|         /// | ||||
|         pub fn connect_if(self: *@This(), from: T, to: T) !bool { | ||||
|             const begin, const end = self.find_segment(from); | ||||
|             if (self.find_connection_in_segment(from, to, begin, end)) |_| { | ||||
|                 return false; | ||||
|             } else { | ||||
|                 try self.map.insert(end, Conn { .from = from, .to = to, }); | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         /// Same to `connect_if`, but returns an error if it's already connected. | ||||
|         pub fn connect(self: *@This(), from: T, to: T) !void { | ||||
|             if (!try self.connect_if(from, to)) { | ||||
|                 return Error.AlreadyConnected; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// | ||||
|         pub fn disconnect_if(self: *@This(), from: T, to: T) bool { | ||||
|             const begin, const end = self.find_segment(from); | ||||
|             if (self.find_connection_in_segment(from, to, begin, end)) |idx| { | ||||
|                 _ = self.map.orderedRemove(idx); | ||||
|                 return true; | ||||
|             } else { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         /// Same to `disconnect_if`, but returns an error if it's not connected. | ||||
|         pub fn disconnect(self: *@This(), from: T, to: T) !void { | ||||
|             if (!self.disconnect_if(from, to)) { | ||||
|                 return Error.NotConnected; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// | ||||
|         pub fn is_connected(self: *const @This(), from: T, to: T) bool { | ||||
|             const begin, const end = self.find_segment(from); | ||||
|             return self.find_connection_in_segment(from, to, begin, end) != null; | ||||
|  | ||||
|         } | ||||
|  | ||||
|         fn find_connection_in_segment(self: *const @This(), from: T, to: T, begin: usize, end: usize) ?usize { | ||||
|             for (self.map.items[begin..end], begin..) |v, idx| { | ||||
|                 if ((v.from == from) and (v.to == to)) { | ||||
|                     return idx; | ||||
|                 } | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
|         fn find_segment(self: *const @This(), from: T) struct { usize, usize } { | ||||
|             const n = self.map.items.len; | ||||
|             if (n == 0) { | ||||
|                 return .{ 0, 0, }; | ||||
|             } | ||||
|  | ||||
|             const base_idx  = self.binsearch(from).?; | ||||
|             const base_from = self.map.items[base_idx].from; | ||||
|  | ||||
|             var begin: usize = undefined; | ||||
|             var end  : usize = undefined; | ||||
|             if (base_from < from) { | ||||
|                 begin = base_idx; | ||||
|                 while ((begin < n) and (self.map.items[begin].from != from)) { begin += 1; } | ||||
|  | ||||
|                 end = begin; | ||||
|                 while ((end < n) and (self.map.items[end].from == from)) { end += 1; } | ||||
|  | ||||
|             } else if (base_from > from) { | ||||
|                 end = base_idx; | ||||
|                 while ((end > 0) and (self.map.items[end-1].from != from)) { end -= 1; } | ||||
|  | ||||
|                 begin = end; | ||||
|                 while ((begin > 0) and (self.map.items[begin-1].from == from)) { begin -= 1; } | ||||
|  | ||||
|             } else { | ||||
|                 begin = base_idx; | ||||
|                 while ((begin > 0) and (self.map.items[begin-1].from == from)) { begin -= 1; } | ||||
|  | ||||
|                 end = base_idx; | ||||
|                 while ((end < n) and (self.map.items[end].from == from)) { end += 1; } | ||||
|             } | ||||
|             return .{ begin, end, }; | ||||
|         } | ||||
|         fn binsearch(self: *const @This(), from: T) ?usize { | ||||
|             if (self.map.items.len == 0) { | ||||
|                 return null; | ||||
|             } | ||||
|  | ||||
|             var left : usize = 0; | ||||
|             var right: usize = self.map.items.len; | ||||
|  | ||||
|             var idx: usize = undefined; | ||||
|             while (left < right) { | ||||
|                 idx = (left + right) / 2; | ||||
|  | ||||
|                 const target = self.map.items[idx].from; | ||||
|                 if (target < from) { | ||||
|                     left = idx + 1; | ||||
|                 } else if (target > from) { | ||||
|                     right = idx -| 1; | ||||
|                 } else { | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|             return idx; | ||||
|         } | ||||
|         fn compare_conn(_: void, a: Conn, b: Conn) bool { | ||||
|             return a.from < b.from; | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| test "check if connected" { | ||||
|     const Sut = Digraph(u8); | ||||
|  | ||||
|     const map = [_]Sut.Conn { | ||||
|         .{ .from = 3, .to = 0, }, | ||||
|         .{ .from = 0, .to = 1, }, | ||||
|         .{ .from = 1, .to = 3, }, | ||||
|     }; | ||||
|  | ||||
|     var sut = try Sut.init(std.testing.allocator, map[0..]); | ||||
|     defer sut.deinit(); | ||||
|  | ||||
|     try std.testing.expect(sut.is_connected(0, 1)); | ||||
|     try std.testing.expect(!sut.is_connected(1, 0)); | ||||
|  | ||||
|     try std.testing.expect(sut.is_connected(1, 3)); | ||||
|     try std.testing.expect(!sut.is_connected(3, 1)); | ||||
|  | ||||
|     try std.testing.expect(sut.is_connected(3, 0)); | ||||
|     try std.testing.expect(!sut.is_connected(0, 3)); | ||||
|  | ||||
|     try std.testing.expect(!sut.is_connected(0, 2)); | ||||
|     try std.testing.expect(!sut.is_connected(2, 0)); | ||||
|  | ||||
|     try std.testing.expect(!sut.is_connected(1, 2)); | ||||
|     try std.testing.expect(!sut.is_connected(2, 1)); | ||||
| } | ||||
| test "make new connection" { | ||||
|     const Sut = Digraph(u8); | ||||
|  | ||||
|     var sut = try Sut.init(std.testing.allocator, &.{}); | ||||
|     defer sut.deinit(); | ||||
|  | ||||
|     try std.testing.expect(try sut.connect_if(2, 1)); | ||||
|  | ||||
|     try std.testing.expect(sut.is_connected(2, 1)); | ||||
|     try std.testing.expect(!sut.is_connected(1, 2)); | ||||
|  | ||||
|     try sut.connect(3, 1); | ||||
|  | ||||
|     try std.testing.expect(sut.is_connected(3, 1)); | ||||
|     try std.testing.expect(!sut.is_connected(1, 3)); | ||||
| } | ||||
| test "making an existing connection fails" { | ||||
|     const Sut = Digraph(u8); | ||||
|  | ||||
|     const map = [_]Sut.Conn { | ||||
|         .{ .from = 0, .to = 1, }, | ||||
|     }; | ||||
|     var sut = try Sut.init(std.testing.allocator, map[0..]); | ||||
|     defer sut.deinit(); | ||||
|  | ||||
|     try std.testing.expect(!try sut.connect_if(0, 1)); | ||||
|     try std.testing.expectError(Sut.Error.AlreadyConnected, sut.connect(0, 1)); | ||||
| } | ||||
| test "disconnect an existing connection" { | ||||
|     const Sut = Digraph(u8); | ||||
|  | ||||
|     const map = [_]Sut.Conn { | ||||
|         .{ .from = 0, .to = 1, }, | ||||
|         .{ .from = 2, .to = 3, }, | ||||
|     }; | ||||
|     var sut = try Sut.init(std.testing.allocator, map[0..]); | ||||
|     defer sut.deinit(); | ||||
|  | ||||
|     try std.testing.expect(sut.disconnect_if(0, 1)); | ||||
|     try std.testing.expect(!sut.is_connected(0, 1)); | ||||
|  | ||||
|     try sut.disconnect(2, 3); | ||||
|     try std.testing.expect(!sut.is_connected(2, 3)); | ||||
| } | ||||
| test "disconnecting a missing connection fails" { | ||||
|     const Sut = Digraph(u8); | ||||
|  | ||||
|     var sut = try Sut.init(std.testing.allocator, &.{}); | ||||
|     defer sut.deinit(); | ||||
|  | ||||
|     try std.testing.expect(!sut.disconnect_if(0, 1)); | ||||
|     try std.testing.expectError(Sut.Error.NotConnected, sut.disconnect(1, 0)); | ||||
| } | ||||
| test "chaotic operation" { | ||||
|     const Sut = Digraph(u16); | ||||
|  | ||||
|     var sut = try Sut.init(std.testing.allocator, &.{}); | ||||
|     defer sut.deinit(); | ||||
|  | ||||
|     const N = 100; | ||||
|     for (0..N) |v| { | ||||
|         const x: Sut.Node = @intCast(v); | ||||
|         try sut.connect(x*%7, x*%13); | ||||
|     } | ||||
|     for (N/2..N) |v| { | ||||
|         const x: Sut.Node = @intCast(v); | ||||
|         try sut.disconnect(x*%7, x*%13); | ||||
|     } | ||||
|     for (0..N/2) |v| { | ||||
|         const x: Sut.Node = @intCast(v); | ||||
|         try std.testing.expect(sut.is_connected(x*%7, x*%13)); | ||||
|     } | ||||
|     for (N/2..N) |v| { | ||||
|         const x: Sut.Node = @intCast(v); | ||||
|         try std.testing.expect(!sut.is_connected(x*%7, x*%13)); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										5
									
								
								src/hncore/root.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/hncore/root.zig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| pub const Digraph = @import("./Digraph.zig").Digraph; | ||||
|  | ||||
| test { | ||||
|     @import("std").testing.refAllDecls(@This()); | ||||
| } | ||||
							
								
								
									
										47
									
								
								src/hnet/App.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/hnet/App.zig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| const std    = @import("std"); | ||||
| const dvui   = @import("dvui"); | ||||
| const hncore = @import("hncore"); | ||||
|  | ||||
| pub const App = struct { | ||||
|     pub fn init(alloc: std.mem.Allocator) !App { | ||||
|         _ = alloc; | ||||
|         return App { | ||||
|         }; | ||||
|     } | ||||
|     pub fn deinit(self: *App) void { | ||||
|         _ = self; | ||||
|     } | ||||
|  | ||||
|     pub fn gui(self: *App) !void { | ||||
|         _ = self; | ||||
|  | ||||
|         try gui_menu(); | ||||
|         try gui_main(); | ||||
|     } | ||||
|     fn gui_menu() !void { | ||||
|         var root = try dvui.menu(@src(), .horizontal, .{ .background = true, .expand = .horizontal }); | ||||
|         defer root.deinit(); | ||||
|  | ||||
|         if (try dvui.menuItemLabel(@src(), "File", .{ .submenu = true }, .{})) |r| { | ||||
|             var float = try dvui.floatingMenu( | ||||
|                 @src(), .{.from = dvui.Rect.fromPoint(dvui.Point{ .x = r.x, .y = r.y + r.h })}, .{}); | ||||
|             defer float.deinit(); | ||||
|  | ||||
|             if (try dvui.menuItemLabel(@src(), "Open", .{}, .{})) |_| { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     fn gui_main() !void { | ||||
|         var box = try dvui.scrollArea(@src(), .{}, .{ | ||||
|             .expand     = .both, | ||||
|             .color_fill = .{ .name = .fill_window }, | ||||
|             .padding    = dvui.Rect.all(8), | ||||
|         }); | ||||
|         defer box.deinit(); | ||||
|  | ||||
|         if (try dvui.button(@src(), "Zoom In", .{}, .{})) { | ||||
|         } | ||||
|         if (try dvui.button(@src(), "Zoom In", .{}, .{})) { | ||||
|         } | ||||
|     } | ||||
| }; | ||||
							
								
								
									
										48
									
								
								src/hnet/main.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/hnet/main.zig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| const std    = @import("std"); | ||||
| const dvui   = @import("dvui"); | ||||
| const hncore = @import("hncore"); | ||||
|  | ||||
| const App = @import("./App.zig").App; | ||||
|  | ||||
| pub fn main() !void { | ||||
|     var gpa = std.heap.GeneralPurposeAllocator(.{}){}; | ||||
|     defer if (gpa.deinit() == .leak) { | ||||
|         @panic("memory leak detected"); | ||||
|     }; | ||||
|  | ||||
|     var backend = try dvui.backend.initWindow(.{ | ||||
|         .allocator = gpa.allocator(), | ||||
|         .size      = .{ .w = 800, .h = 600, }, | ||||
|         .min_size  = .{ .w = 250, .h = 350, }, | ||||
|         .vsync     = true, | ||||
|         .title     = "Heaven's Net", | ||||
|     }); | ||||
|     defer backend.deinit(); | ||||
|  | ||||
|     var win = try dvui.Window.init(@src(), gpa.allocator(), backend.backend(), .{}); | ||||
|     defer win.deinit(); | ||||
|  | ||||
|     var app = try App.init(gpa.allocator()); | ||||
|     while (true) { | ||||
|         try win.begin( | ||||
|             win.beginWait(backend.hasEvent()), | ||||
|         ); | ||||
|  | ||||
|         const quit = try backend.addAllEvents(&win); | ||||
|         if (quit) { | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         try app.gui(); | ||||
|  | ||||
|         const end_micros = try win.end(.{}); | ||||
|  | ||||
|         backend.setCursor(win.cursorRequested()); | ||||
|         backend.textInputRect(win.textInputRequested()); | ||||
|         backend.renderPresent(); | ||||
|  | ||||
|         backend.waitEventTimeout( | ||||
|             win.waitTime(end_micros, null), | ||||
|         ); | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user