creates new project
This commit is contained in:
parent
1b89a05bbf
commit
e1adeb52fe
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),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user