tiny.filigree.font.variations
Defined in font.
API (28)
Actions
Public operations.
Avar.initAvar.mapFvar.axisFvar.axisIndexFvar.initHvar.advanceWidthDeltaHvar.initItemVariationStore.deltaItemVariationStore.initItemVariationStore.itemVariationDataMvar.initMvar.valueDeltaVvar.advanceHeightDeltaVvar.initclampDesignMetricclampI32fixed16FromFloatfixed16ToF2Dot14normalizeVariationAxisvariationChildOffsetvariationChildTable
Types and contracts
Public types and contracts.
Source
Source: lib/filigree/src/font/root.zig:18
zig
pub const variations = @import("variations.zig");Source: lib/filigree/src/font/variations.zig
zig
const std = @import("std");const binary = @import("binary.zig");const model = @import("model.zig");const FontError = model.FontError;const Table = binary.Table;const Tag = model.Tag;const VariationAxis = model.VariationAxis;const VariationSetting = model.VariationSetting;const readI8 = binary.readI8;const readI16 = binary.readI16;const readI32 = binary.readI32;const readU16 = binary.readU16;const readU32 = binary.readU32;pub const Fvar = struct { table: Table, axes_offset: usize, axis_count: u16, axis_size: u16, pub fn init(data: []const u8, fvar_table: Table) FontError!Fvar { if (fvar_table.len < 16) return error.InvalidVariations; const major = try readU16(data, fvar_table.offset); const minor = try readU16(data, fvar_table.offset + 2); if (major != 1 or minor != 0) return error.InvalidVariations; const axes_array_offset = try readU16(data, fvar_table.offset + 4); const axis_count = try readU16(data, fvar_table.offset + 8); const axis_size = try readU16(data, fvar_table.offset + 10); const instance_count = try readU16(data, fvar_table.offset + 12); const instance_size = try readU16(data, fvar_table.offset + 14); if (axis_count == 0 or axis_size < 20) return error.InvalidVariations; if (axes_array_offset > fvar_table.len) return error.InvalidVariations; const axes_offset = fvar_table.offset + axes_array_offset; const axes_len = @as(usize, axis_count) * @as(usize, axis_size); if (axes_len > fvar_table.len - axes_array_offset) return error.InvalidVariations; const instances_offset = axes_array_offset + axes_len; const minimum_instance_size = @as(usize, axis_count) * 4 + 4; if (instance_count != 0 and instance_size < minimum_instance_size) return error.InvalidVariations; const instances_len = @as(usize, instance_count) * @as(usize, instance_size); if (instances_len > fvar_table.len - instances_offset) return error.InvalidVariations; var result = Fvar{ .table = fvar_table, .axes_offset = axes_offset, .axis_count = axis_count, .axis_size = axis_size, }; for (0..axis_count) |axis_index| { _ = try result.axis(data, axis_index); } return result; } pub fn axis(self: Fvar, data: []const u8, axis_index: usize) FontError!VariationAxis { if (axis_index >= self.axis_count) return error.InvalidVariations; const offset = self.axes_offset + axis_index * @as(usize, self.axis_size); const variation_axis = VariationAxis{ .tag = try readU32(data, offset), .min_value = try readI32(data, offset + 4), .default_value = try readI32(data, offset + 8), .max_value = try readI32(data, offset + 12), .flags = try readU16(data, offset + 16), .name_id = try readU16(data, offset + 18), }; if (variation_axis.min_value > variation_axis.default_value or variation_axis.default_value > variation_axis.max_value) return error.InvalidVariations; return variation_axis; } pub fn axisIndex(self: Fvar, data: []const u8, axis_tag: Tag) FontError!?usize { for (0..self.axis_count) |axis_index| { const variation_axis = try self.axis(data, axis_index); if (variation_axis.tag == axis_tag) return axis_index; } return null; }};pub const Avar = struct { table: Table, axis_count: u16, pub fn init(data: []const u8, avar_table: Table, expected_axis_count: u16) FontError!Avar { if (avar_table.len < 8) return error.InvalidVariations; const major = try readU16(data, avar_table.offset); const minor = try readU16(data, avar_table.offset + 2); if (major != 1 or minor != 0) return error.InvalidVariations; const axis_count = try readU16(data, avar_table.offset + 6); if (axis_count != expected_axis_count) return error.InvalidVariations; var cursor = avar_table.offset + 8; const end = avar_table.offset + avar_table.len; for (0..axis_count) |_| { if (cursor > end or end - cursor < 2) return error.InvalidVariations; const position_map_count = try readU16(data, cursor); cursor += 2; const records_len = @as(usize, position_map_count) * 4; if (records_len > end - cursor) return error.InvalidVariations; cursor += records_len; } return .{ .table = avar_table, .axis_count = axis_count }; } pub fn map(self: Avar, data: []const u8, axis_index: usize, value: i32) FontError!i32 { const map_offset = try self.segmentMapOffset(data, axis_index); const count = try readU16(data, map_offset); if (count < 3) return value; const records = map_offset + 2; if (!try avarHasRequiredMaps(data, records, count)) return value; var previous_valid: ?AvarMap = null; var start: ?AvarMap = null; var end: ?AvarMap = null; for (0..count) |map_index| { const map_record = try avarMapAt(data, records, map_index); if (previous_valid) |previous| { if (map_record.from <= previous.from or map_record.to < previous.to) continue; } previous_valid = map_record; if (map_record.from <= value) start = map_record; if (map_record.from >= value) { end = map_record; break; } } const start_map = start orelse return value; const end_map = end orelse return value; if (start_map.from == value) return start_map.to; if (end_map.from == value) return end_map.to; if (start_map.from == end_map.from) return value; const numerator = @as(i64, value - start_map.from) * @as(i64, end_map.to - start_map.to); const denominator = @as(i64, end_map.from - start_map.from); const mapped = @as(i64, start_map.to) + @divTrunc(numerator, denominator); return clampFixed16(mapped); } fn segmentMapOffset(self: Avar, data: []const u8, axis_index: usize) FontError!usize { if (axis_index >= self.axis_count) return error.InvalidVariations; var cursor = self.table.offset + 8; for (0..axis_index) |_| { const count = try readU16(data, cursor); cursor += 2 + @as(usize, count) * 4; } return cursor; }};const AvarMap = struct { from: i32, to: i32,};pub const DeltaSetIndex = struct { outer: u32, inner: u32,};pub const Hvar = struct { store: ItemVariationStore, advance_map: ?DeltaSetIndexMap, pub fn init(data: []const u8, hvar_table: Table, axis_count: u16) FontError!Hvar { if (hvar_table.len < 20) return error.InvalidVariations; const major = try readU16(data, hvar_table.offset); const minor = try readU16(data, hvar_table.offset + 2); if (major != 1 or minor != 0) return error.InvalidVariations; const store_table = try variationChildTable(hvar_table, try readU32(data, hvar_table.offset + 4), 8); const store = try ItemVariationStore.init(data, store_table, axis_count); const advance_map = try optionalDeltaSetIndexMap(data, hvar_table, try readU32(data, hvar_table.offset + 8)); _ = try optionalDeltaSetIndexMap(data, hvar_table, try readU32(data, hvar_table.offset + 12)); _ = try optionalDeltaSetIndexMap(data, hvar_table, try readU32(data, hvar_table.offset + 16)); return .{ .store = store, .advance_map = advance_map, }; } pub fn advanceWidthDelta(self: Hvar, data: []const u8, face: anytype, settings: []const VariationSetting, glyph_id: u32) FontError!i32 { const delta_index = if (self.advance_map) |mapping| try mapping.index(data, glyph_id) else DeltaSetIndex{ .outer = 0, .inner = glyph_id }; return try self.store.delta(data, face, settings, delta_index); }};pub const Vvar = struct { store: ItemVariationStore, advance_map: ?DeltaSetIndexMap, pub fn init(data: []const u8, vvar_table: Table, axis_count: u16) FontError!Vvar { if (vvar_table.len < 24) return error.InvalidVariations; const major = try readU16(data, vvar_table.offset); const minor = try readU16(data, vvar_table.offset + 2); if (major != 1 or minor != 0) return error.InvalidVariations; const store_table = try variationChildTable(vvar_table, try readU32(data, vvar_table.offset + 4), 8); const store = try ItemVariationStore.init(data, store_table, axis_count); const advance_map = try optionalDeltaSetIndexMap(data, vvar_table, try readU32(data, vvar_table.offset + 8)); _ = try optionalDeltaSetIndexMap(data, vvar_table, try readU32(data, vvar_table.offset + 12)); _ = try optionalDeltaSetIndexMap(data, vvar_table, try readU32(data, vvar_table.offset + 16)); _ = try optionalDeltaSetIndexMap(data, vvar_table, try readU32(data, vvar_table.offset + 20)); return .{ .store = store, .advance_map = advance_map, }; } pub fn advanceHeightDelta(self: Vvar, data: []const u8, face: anytype, settings: []const VariationSetting, glyph_id: u32) FontError!i32 { const delta_index = if (self.advance_map) |mapping| try mapping.index(data, glyph_id) else DeltaSetIndex{ .outer = 0, .inner = glyph_id }; return try self.store.delta(data, face, settings, delta_index); }};pub const Mvar = struct { store: ?ItemVariationStore, records_offset: usize, value_record_size: u16, value_record_count: u16, pub fn init(data: []const u8, mvar_table: Table, axis_count: u16) FontError!Mvar { if (mvar_table.len < 12) return error.InvalidVariations; const major = try readU16(data, mvar_table.offset); const minor = try readU16(data, mvar_table.offset + 2); const reserved = try readU16(data, mvar_table.offset + 4); if (major != 1 or minor != 0 or reserved != 0) return error.InvalidVariations; const value_record_size = try readU16(data, mvar_table.offset + 6); const value_record_count = try readU16(data, mvar_table.offset + 8); const store_offset = try readU16(data, mvar_table.offset + 10); if (value_record_size < 8) return error.InvalidVariations; const records_len = @as(u64, value_record_count) * @as(u64, value_record_size); if (records_len > @as(u64, mvar_table.len - 12)) return error.InvalidVariations; const records_len_usize: usize = @intCast(records_len); const store = if (value_record_count == 0) blk: { if (store_offset != 0) return error.InvalidVariations; break :blk null; } else blk: { if (store_offset == 0) return error.InvalidVariations; const store_offset_usize: usize = store_offset; if (store_offset_usize < 12 + records_len_usize) return error.InvalidVariations; break :blk try ItemVariationStore.init( data, try variationChildTable(mvar_table, store_offset, 8), axis_count, ); }; const result = Mvar{ .store = store, .records_offset = mvar_table.offset + 12, .value_record_size = value_record_size, .value_record_count = value_record_count, }; var previous: ?Tag = null; for (0..value_record_count) |record_index| { const current = try readU32(data, result.recordOffset(record_index)); if (previous) |previous_tag| { if (current <= previous_tag) return error.InvalidVariations; } previous = current; } return result; } pub fn valueDelta(self: Mvar, data: []const u8, face: anytype, settings: []const VariationSetting, metric_tag: Tag) FontError!i32 { const store = self.store orelse return 0; var left: usize = 0; var right: usize = self.value_record_count; while (left < right) { const mid = left + (right - left) / 2; const offset = self.recordOffset(mid); const current = try readU32(data, offset); if (metric_tag < current) { right = mid; } else if (metric_tag > current) { left = mid + 1; } else { return try store.delta(data, face, settings, .{ .outer = try readU16(data, offset + 4), .inner = try readU16(data, offset + 6), }); } } return 0; } fn recordOffset(self: Mvar, record_index: usize) usize { return self.records_offset + record_index * @as(usize, self.value_record_size); }};const DeltaSetIndexMap = struct { table: Table, map_count: u32, data_offset: usize, entry_size: u8, inner_bit_count: u5, pub fn init(data: []const u8, map_table: Table) FontError!DeltaSetIndexMap { if (map_table.len < 4) return error.InvalidVariations; const format = data[map_table.offset]; const entry_format = data[map_table.offset + 1]; if ((entry_format & 0xc0) != 0) return error.InvalidVariations; const entry_size: u8 = ((entry_format >> 4) & 0x03) + 1; const inner_bit_count: u5 = @intCast((entry_format & 0x0f) + 1); const header_len: usize = switch (format) { 0 => 4, 1 => 6, else => return error.InvalidVariations, }; if (map_table.len < header_len) return error.InvalidVariations; const map_count = switch (format) { 0 => @as(u32, try readU16(data, map_table.offset + 2)), 1 => try readU32(data, map_table.offset + 2), else => unreachable, }; if (map_count == 0) return error.InvalidVariations; if (@as(u64, map_count) * @as(u64, entry_size) > @as(u64, map_table.len - header_len)) return error.InvalidVariations; return .{ .table = map_table, .map_count = map_count, .data_offset = map_table.offset + header_len, .entry_size = entry_size, .inner_bit_count = inner_bit_count, }; } pub fn index(self: DeltaSetIndexMap, data: []const u8, glyph_id: u32) FontError!DeltaSetIndex { const entry_index = if (glyph_id < self.map_count) glyph_id else self.map_count - 1; const entry_offset = self.data_offset + @as(usize, @intCast(entry_index)) * @as(usize, self.entry_size); if (entry_offset < self.table.offset or entry_offset > self.table.offset + self.table.len or @as(usize, self.entry_size) > self.table.offset + self.table.len - entry_offset) return error.InvalidVariations; var packed_index: u32 = 0; for (0..self.entry_size) |byte_index| { packed_index = (packed_index << 8) | @as(u32, data[entry_offset + byte_index]); } const inner_mask = (@as(u32, 1) << self.inner_bit_count) - 1; return .{ .outer = packed_index >> self.inner_bit_count, .inner = packed_index & inner_mask, }; }};pub const ItemVariationStore = struct { table: Table, region_list: VariationRegionList, data_count: u16, data_offsets_offset: usize, pub fn init(data: []const u8, store_table: Table, axis_count: u16) FontError!ItemVariationStore { if (store_table.len < 8) return error.InvalidVariations; if (try readU16(data, store_table.offset) != 1) return error.InvalidVariations; const region_list = try VariationRegionList.init( data, try variationChildTable(store_table, try readU32(data, store_table.offset + 2), 4), axis_count, ); const data_count = try readU16(data, store_table.offset + 6); if (@as(u64, data_count) * 4 > @as(u64, store_table.len - 8)) return error.InvalidVariations; const result = ItemVariationStore{ .table = store_table, .region_list = region_list, .data_count = data_count, .data_offsets_offset = store_table.offset + 8, }; for (0..data_count) |data_index| { const item_data = try result.itemVariationData(data, data_index); if (item_data) |subtable| _ = try ItemVariationData.init(data, subtable, region_list.region_count); } return result; } pub fn delta(self: ItemVariationStore, data: []const u8, face: anytype, settings: []const VariationSetting, index: DeltaSetIndex) FontError!i32 { if (index.outer >= self.data_count) return 0; const item_data_table = (try self.itemVariationData(data, @intCast(index.outer))) orelse return 0; const item_data = try ItemVariationData.init(data, item_data_table, self.region_list.region_count); return try item_data.delta(data, self.region_list, face, settings, index.inner); } pub fn itemVariationData(self: ItemVariationStore, data: []const u8, data_index: usize) FontError!?Table { if (data_index >= self.data_count) return null; const relative_offset = try readU32(data, self.data_offsets_offset + data_index * 4); if (relative_offset == 0) return null; return try variationChildTable(self.table, relative_offset, 6); }};const VariationRegionList = struct { table: Table, axis_count: u16, region_count: u16, pub fn init(data: []const u8, region_table: Table, expected_axis_count: u16) FontError!VariationRegionList { if (region_table.len < 4) return error.InvalidVariations; const axis_count = try readU16(data, region_table.offset); const region_count = try readU16(data, region_table.offset + 2); if (axis_count != expected_axis_count) return error.InvalidVariations; if ((region_count & 0x8000) != 0) return error.InvalidVariations; const region_record_len = @as(usize, axis_count) * 6; if (@as(u64, region_count) * @as(u64, region_record_len) > @as(u64, region_table.len - 4)) return error.InvalidVariations; return .{ .table = region_table, .axis_count = axis_count, .region_count = region_count, }; } pub fn scalar(self: VariationRegionList, data: []const u8, face: anytype, settings: []const VariationSetting, region_index: u16) FontError!i32 { if (region_index >= self.region_count) return error.InvalidVariations; const region_offset = self.table.offset + 4 + @as(usize, region_index) * @as(usize, self.axis_count) * 6; var region_scalar: i64 = variationScalarOne; for (0..self.axis_count) |axis_index| { const axis_offset = region_offset + axis_index * 6; const axis_scalar = variationAxisScalar( try readI16(data, axis_offset), try readI16(data, axis_offset + 2), try readI16(data, axis_offset + 4), try face.normalizedCoordinateAt(settings, axis_index), ); region_scalar = @divTrunc(region_scalar * axis_scalar, variationScalarOne); if (region_scalar == 0) return 0; } return @intCast(region_scalar); }};const ItemVariationData = struct { item_count: u16, word_delta_count: u16, region_index_count: u16, long_words: bool, region_indexes_offset: usize, deltas_offset: usize, row_len: usize, pub fn init(data: []const u8, item_table: Table, region_count: u16) FontError!ItemVariationData { if (item_table.len < 6) return error.InvalidVariations; const item_count = try readU16(data, item_table.offset); const packed_word_delta_count = try readU16(data, item_table.offset + 2); const long_words = (packed_word_delta_count & 0x8000) != 0; const word_delta_count = packed_word_delta_count & 0x7fff; const region_index_count = try readU16(data, item_table.offset + 4); if (word_delta_count > region_index_count) return error.InvalidVariations; const region_indexes_offset = item_table.offset + 6; const region_indexes_len = @as(usize, region_index_count) * 2; if (region_indexes_len > item_table.len - 6) return error.InvalidVariations; for (0..region_index_count) |region_index_offset| { const region_index = try readU16(data, region_indexes_offset + region_index_offset * 2); if (region_index >= region_count) return error.InvalidVariations; } const deltas_offset = region_indexes_offset + region_indexes_len; const row_units = @as(usize, region_index_count) + @as(usize, word_delta_count); const row_len = if (long_words) row_units * 2 else row_units; if (@as(u64, item_count) * @as(u64, row_len) > @as(u64, item_table.offset + item_table.len - deltas_offset)) return error.InvalidVariations; return .{ .item_count = item_count, .word_delta_count = word_delta_count, .region_index_count = region_index_count, .long_words = long_words, .region_indexes_offset = region_indexes_offset, .deltas_offset = deltas_offset, .row_len = row_len, }; } pub fn delta(self: ItemVariationData, data: []const u8, region_list: VariationRegionList, face: anytype, settings: []const VariationSetting, inner_index: u32) FontError!i32 { if (inner_index >= self.item_count) return 0; const row_offset = self.deltas_offset + @as(usize, @intCast(inner_index)) * self.row_len; var accumulated: i64 = 0; for (0..self.region_index_count) |delta_index| { const delta_value = try self.deltaValue(data, row_offset, delta_index); if (delta_value == 0) continue; const region_index = try readU16(data, self.regionIndexesOffset(delta_index)); const scalar = try region_list.scalar(data, face, settings, region_index); accumulated += @as(i64, delta_value) * @as(i64, scalar); } return roundVariationDelta(accumulated); } fn deltaValue(self: ItemVariationData, data: []const u8, row_offset: usize, delta_index: usize) FontError!i32 { if (self.long_words) { if (delta_index < self.word_delta_count) return try readI32(data, row_offset + delta_index * 4); const offset = row_offset + @as(usize, self.word_delta_count) * 4 + (delta_index - @as(usize, self.word_delta_count)) * 2; return try readI16(data, offset); } if (delta_index < self.word_delta_count) return try readI16(data, row_offset + delta_index * 2); const offset = row_offset + @as(usize, self.word_delta_count) * 2 + (delta_index - @as(usize, self.word_delta_count)); return try readI8(data, offset); } fn regionIndexesOffset(self: ItemVariationData, region_index: usize) usize { return self.region_indexes_offset + region_index * 2; }};fn optionalDeltaSetIndexMap(data: []const u8, parent: Table, relative_offset: u32) FontError!?DeltaSetIndexMap { if (relative_offset == 0) return null; return try DeltaSetIndexMap.init(data, try variationChildTable(parent, relative_offset, 4));}pub fn variationChildTable(parent: Table, relative_offset: u32, min_len: usize) FontError!Table { const offset = try variationChildOffset(parent, relative_offset, min_len); return .{ .offset = offset, .len = parent.offset + parent.len - offset };}pub fn variationChildOffset(parent: Table, relative_offset: u32, min_len: usize) FontError!usize { if (relative_offset == 0) return error.InvalidVariations; if (relative_offset > std.math.maxInt(usize)) return error.InvalidVariations; const relative: usize = @intCast(relative_offset); if (relative > parent.len or min_len > parent.len - relative) return error.InvalidVariations; return parent.offset + relative;}pub fn fixed16FromFloat(value: f32) FontError!i32 { if (!std.math.isFinite(value)) return error.InvalidVariations; const scaled = @as(f64, value) * 65536.0; if (scaled > @as(f64, @floatFromInt(std.math.maxInt(i32)))) return error.InvalidVariations; if (scaled < @as(f64, @floatFromInt(std.math.minInt(i32)))) return error.InvalidVariations; const rounded = if (scaled >= 0) @floor(scaled + 0.5) else @ceil(scaled - 0.5); return @intFromFloat(rounded);}pub fn normalizeVariationAxis(axis: VariationAxis, user_value: i32) i32 { const clamped_user = std.math.clamp(user_value, axis.min_value, axis.max_value); if (clamped_user < axis.default_value) { const denominator = @as(i64, axis.default_value) - @as(i64, axis.min_value); if (denominator == 0) return 0; const numerator = (@as(i64, axis.default_value) - @as(i64, clamped_user)) * 65536; return clampFixed16(-@divTrunc(numerator, denominator)); } if (clamped_user > axis.default_value) { const denominator = @as(i64, axis.max_value) - @as(i64, axis.default_value); if (denominator == 0) return 0; const numerator = (@as(i64, clamped_user) - @as(i64, axis.default_value)) * 65536; return clampFixed16(@divTrunc(numerator, denominator)); } return 0;}pub fn fixed16ToF2Dot14(value: i32) i16 { const clamped = std.math.clamp(value, -65536, 65536); return @intCast((clamped + 2) >> 2);}fn f2Dot14ToFixed16(value: i16) i32 { return @as(i32, value) << 2;}fn clampFixed16(value: i64) i32 { if (value > 65536) return 65536; if (value < -65536) return -65536; return @intCast(value);}const variationScalarOne: i32 = 65536;const variationScalarHalf: i64 = variationScalarOne / 2;fn roundVariationDelta(value: i64) i32 { const rounded = @divFloor(value + variationScalarHalf, variationScalarOne); return clampI32(rounded);}fn variationAxisScalar(start: i16, peak: i16, end: i16, instance: i16) i32 { if (start > peak or peak > end) return variationScalarOne; if (start < 0 and end > 0 and peak != 0) return variationScalarOne; if (peak == 0) return variationScalarOne; if (instance < start or instance > end) return 0; if (instance == peak) return variationScalarOne; if (instance < peak) { const numerator = (@as(i64, instance) - @as(i64, start)) * variationScalarOne; const denominator = @as(i64, peak) - @as(i64, start); if (denominator <= 0) return 0; return @intCast(@divTrunc(numerator, denominator)); } const numerator = (@as(i64, end) - @as(i64, instance)) * variationScalarOne; const denominator = @as(i64, end) - @as(i64, peak); if (denominator <= 0) return 0; return @intCast(@divTrunc(numerator, denominator));}pub fn clampDesignMetric(base: i32, delta: i32) i32 { const value = @as(i64, base) + @as(i64, delta); if (value <= 0) return 0; if (value > std.math.maxInt(i32)) return std.math.maxInt(i32); return @intCast(value);}pub fn clampI32(value: i64) i32 { if (value > std.math.maxInt(i32)) return std.math.maxInt(i32); if (value < std.math.minInt(i32)) return std.math.minInt(i32); return @intCast(value);}fn avarMapAt(data: []const u8, records: usize, map_index: usize) FontError!AvarMap { const offset = records + map_index * 4; return .{ .from = f2Dot14ToFixed16(try readI16(data, offset)), .to = f2Dot14ToFixed16(try readI16(data, offset + 2)), };}fn avarHasRequiredMaps(data: []const u8, records: usize, count: u16) FontError!bool { var has_min = false; var has_default = false; var has_max = false; for (0..count) |map_index| { const map_record = try avarMapAt(data, records, map_index); if (map_record.from == -65536 and map_record.to == -65536) has_min = true; if (map_record.from == 0 and map_record.to == 0) has_default = true; if (map_record.from == 65536 and map_record.to == 65536) has_max = true; } return has_min and has_default and has_max;}test "item variation deltas round at use" { try std.testing.expectEqual(@as(i32, 0), roundVariationDelta(variationScalarHalf - 1)); try std.testing.expectEqual(@as(i32, 1), roundVariationDelta(variationScalarHalf)); try std.testing.expectEqual(@as(i32, 2), roundVariationDelta(variationScalarOne + variationScalarHalf)); try std.testing.expectEqual(@as(i32, 0), roundVariationDelta(-variationScalarHalf)); try std.testing.expectEqual(@as(i32, -1), roundVariationDelta(-variationScalarHalf - 1)); try std.testing.expectEqual(@as(i32, -1), roundVariationDelta(-variationScalarOne - variationScalarHalf)); try std.testing.expectEqual(@as(i32, -2), roundVariationDelta(-variationScalarOne - variationScalarHalf - 1));}Audit
| Definitions | 29 |
|---|---|
| Public names | 29 |
| Members | 20 |
| Version | 26.7.0 |
| Revision | daab053ee433 |