lib/arrange/src/types.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 
  3 pub const Size = struct {
  4     width: f32 = 0,
  5     height: f32 = 0,
  6 };
  7 
  8 pub const Rect = struct {
  9     x: f32 = 0,
 10     y: f32 = 0,
 11     width: f32 = 0,
 12     height: f32 = 0,
 13 };
 14 
 15 pub const Dimensions = struct {
 16     width: ?f32 = null,
 17     height: ?f32 = null,
 18 };
 19 
 20 pub const Insets = struct {
 21     top: ?f32 = null,
 22     left: ?f32 = null,
 23     right: ?f32 = null,
 24     bottom: ?f32 = null,
 25 };
 26 
 27 pub const Constraints = struct {
 28     min_width: ?f32 = null,
 29     min_height: ?f32 = null,
 30     max_width: ?f32 = null,
 31     max_height: ?f32 = null,
 32 };
 33 
 34 pub const FlexDirection = enum {
 35     row,
 36     column,
 37 };
 38 
 39 pub const Wrap = enum {
 40     no_wrap,
 41     wrap,
 42 };
 43 
 44 pub const JustifyContent = enum {
 45     flex_start,
 46     flex_end,
 47     center,
 48     space_between,
 49     space_around,
 50     space_evenly,
 51 };
 52 
 53 pub const AlignItems = enum {
 54     flex_start,
 55     flex_end,
 56     center,
 57     stretch,
 58 };
 59 
 60 pub const AlignSelf = enum {
 61     auto,
 62     flex_start,
 63     flex_end,
 64     center,
 65     stretch,
 66 };
 67 
 68 pub const Position = enum {
 69     relative,
 70     absolute,
 71 };
 72 
 73 pub const Style = struct {
 74     flex_direction: FlexDirection = .row,
 75     wrap: Wrap = .no_wrap,
 76     justify_content: JustifyContent = .flex_start,
 77     align_items: AlignItems = .stretch,
 78     align_self: AlignSelf = .auto,
 79     gap: f32 = 0,
 80     padding: Insets = .{},
 81     constraints: Constraints = .{},
 82     flex_grow: f32 = 0,
 83     flex_shrink: f32 = 1,
 84     flex_basis: ?f32 = null,
 85     position: Position = .relative,
 86     inset: Insets = .{},
 87 };
 88 
 89 pub const AvailableSpace = union(enum) {
 90     definite: f32,
 91     min_content,
 92     max_content,
 93 };
 94 
 95 pub const Available = struct {
 96     width: AvailableSpace = .max_content,
 97     height: AvailableSpace = .max_content,
 98 };
 99 
100 pub const LayoutInput = struct {
101     known: Dimensions = .{},
102     available: Available = .{},
103 };
104 
105 pub const Measure = struct {
106     context: ?*anyopaque = null,
107     call: *const fn (context: ?*anyopaque, token: u32, available: Available) Size,
108 
109     pub fn size(self: Measure, token: u32, available: Available) Size {
110         validateAvailable(available);
111         const measured = self.call(self.context, token, available);
112         std.debug.assert(std.math.isFinite(measured.width));
113         std.debug.assert(std.math.isFinite(measured.height));
114         std.debug.assert(measured.width >= 0);
115         std.debug.assert(measured.height >= 0);
116         return measured;
117     }
118 };
119 
120 pub const MeasureMemo = struct {
121     valid: bool = false,
122     token: u32 = 0,
123     available: Available = .{},
124     size: Size = .{},
125 };
126 
127 pub const Measurer = struct {
128     measure: Measure,
129     memos: []MeasureMemo = &.{},
130     used: usize = 0,
131     calls: usize = 0,
132     probes: usize = 0,
133 
134     pub fn reset(self: *Measurer) void {
135         for (self.memos) |*memo| memo.* = .{};
136         self.used = 0;
137         self.calls = 0;
138         self.probes = 0;
139     }
140 
141     pub fn size(self: *Measurer, token: u32, available: Available) Size {
142         if (self.memos.len == 0) {
143             self.calls += 1;
144             return self.measure.size(token, available);
145         }
146         const index = measureHash(token, available) % self.memos.len;
147         const memo = &self.memos[index];
148         self.probes += 1;
149         if (memo.valid and memo.token == token and sameAvailable(memo.available, available)) {
150             return memo.size;
151         }
152 
153         const measured = self.measure.size(token, available);
154         self.calls += 1;
155         if (!memo.valid) {
156             self.used += 1;
157         }
158         memo.* = .{
159             .valid = true,
160             .token = token,
161             .available = available,
162             .size = measured,
163         };
164         return measured;
165     }
166 };
167 
168 fn validateAvailable(available: Available) void {
169     validateSpace(available.width);
170     validateSpace(available.height);
171 }
172 
173 fn validateSpace(space: AvailableSpace) void {
174     switch (space) {
175         .definite => |value| {
176             std.debug.assert(std.math.isFinite(value));
177             std.debug.assert(value >= 0);
178         },
179         else => {},
180     }
181 }
182 
183 fn measureHash(token: u32, available: Available) usize {
184     var hash = @as(u64, token) *% 0x9e3779b97f4a7c15;
185     hash = mixHash(hash, spaceHash(available.width));
186     hash = mixHash(hash, spaceHash(available.height));
187     return @intCast(hash ^ (hash >> 32));
188 }
189 
190 fn mixHash(hash: u64, value: u64) u64 {
191     return (hash ^ value) *% 0xbf58476d1ce4e5b9;
192 }
193 
194 fn spaceHash(space: AvailableSpace) u64 {
195     return switch (space) {
196         .min_content => 0x243f6a8885a308d3,
197         .max_content => 0x13198a2e03707344,
198         .definite => |value| 0xa4093822299f31d0 ^ normalizedFloatBits(value),
199     };
200 }
201 
202 fn normalizedFloatBits(value: f32) u64 {
203     if (value == 0) return 0;
204     const bits: u32 = @bitCast(value);
205     return bits;
206 }
207 
208 fn sameAvailable(left: Available, right: Available) bool {
209     return sameSpace(left.width, right.width) and sameSpace(left.height, right.height);
210 }
211 
212 fn sameSpace(left: AvailableSpace, right: AvailableSpace) bool {
213     return switch (left) {
214         .definite => |value| switch (right) {
215             .definite => |other| value == other,
216             else => false,
217         },
218         .min_content => right == .min_content,
219         .max_content => right == .max_content,
220     };
221 }
222 
223 pub const Node = struct {
224     id: usize = 0,
225     style: Style = .{},
226     size: Dimensions = .{},
227     intrinsic_size: Size = .{},
228     measure_token: ?u32 = null,
229     clip_x: bool = false,
230     clip_y: bool = false,
231     children: []const Node = &.{},
232 };
233 
234 pub const LayoutResult = struct {
235     id: usize,
236     rect: Rect,
237     children: []LayoutResult,
238 };