lib/zen/src/diagram/category.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 const std = @import("std");
 2 
 3 const model = @import("model.zig");
 4 const root = @import("root.zig");
 5 
 6 const spec = root.spec;
 7 const Bounds = model.Bounds;
 8 
 9 pub const label_buffer_bytes: usize = 512;
10 
11 pub fn appendAssumeCapacity(bounds: *Bounds, label_value: []const u8) void {
12     for (bounds.categories.items) |item| {
13         if (std.mem.eql(u8, item, label_value)) return;
14     }
15     bounds.categories.appendAssumeCapacity(label_value);
16 }
17 
18 pub fn label(buffer: *[label_buffer_bytes]u8, value: spec.XValue) []const u8 {
19     return switch (value) {
20         .number => |number| std.fmt.bufPrint(buffer, "{d:.3}", .{number}) catch unreachable,
21         .text => |text| text,
22     };
23 }
24 
25 pub fn coordinate(label_value: []const u8, bounds: Bounds, left: f64, right: f64) f64 {
26     const count = @max(bounds.categories.items.len, 1);
27     const slot = (right - left) / @as(f64, @floatFromInt(count));
28     for (bounds.categories.items, 0..) |item, index| {
29         if (std.mem.eql(u8, item, label_value)) return left + slot * (@as(f64, @floatFromInt(index)) + 0.5);
30     }
31     return left + slot * 0.5;
32 }
33 
34 test "category labels fit every finite f64 without allocation" {
35     var buffer: [label_buffer_bytes]u8 = undefined;
36     try std.testing.expect(label(&buffer, .{ .number = std.math.floatMax(f64) }).len < buffer.len);
37     try std.testing.expect(label(&buffer, .{ .number = -std.math.floatMax(f64) }).len < buffer.len);
38     try std.testing.expectEqualStrings("borrowed", label(&buffer, .{ .text = @constCast("borrowed") }));
39 }