add Curve and start polishing window resize
This commit is contained in:
parent
7e9ea6e641
commit
52a4b4ee59
1 changed files with 197 additions and 30 deletions
227
src/main.zig
227
src/main.zig
|
@ -3,13 +3,13 @@ pub fn main() !void {
|
||||||
defer _ = gpa.deinit();
|
defer _ = gpa.deinit();
|
||||||
const allocator = gpa.allocator();
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
raylib.SetConfigFlags(raylib.FLAG_VSYNC_HINT); // | raylib.FLAG_WINDOW_RESIZABLE);
|
raylib.SetConfigFlags(raylib.FLAG_VSYNC_HINT | raylib.FLAG_WINDOW_RESIZABLE);
|
||||||
raylib.InitWindow(@intFromFloat(screen_width), @intFromFloat(screen_height), "ReX");
|
raylib.InitWindow(@intFromFloat(screen_width), @intFromFloat(screen_height), "ReX");
|
||||||
defer raylib.CloseWindow();
|
defer raylib.CloseWindow();
|
||||||
|
|
||||||
raylib.SetWindowMinSize(480, 272);
|
raylib.SetWindowMinSize(480, 272);
|
||||||
raylib.SetWindowMaxSize(1920, 1080);
|
raylib.SetWindowMaxSize(1920, 1080);
|
||||||
scales.recalculate();
|
scales = Scales.init();
|
||||||
|
|
||||||
global_font = raylib.LoadFontEx("font/SCE-PS3-RD-R-LATIN.TTF", 32, 0, 250);
|
global_font = raylib.LoadFontEx("font/SCE-PS3-RD-R-LATIN.TTF", 32, 0, 250);
|
||||||
raylib.SetTextureFilter(global_font.texture, raylib.TEXTURE_FILTER_BILINEAR);
|
raylib.SetTextureFilter(global_font.texture, raylib.TEXTURE_FILTER_BILINEAR);
|
||||||
|
@ -54,6 +54,7 @@ pub fn main() !void {
|
||||||
screen_width = @floatFromInt(raylib.GetScreenWidth());
|
screen_width = @floatFromInt(raylib.GetScreenWidth());
|
||||||
screen_height = @floatFromInt(raylib.GetScreenHeight());
|
screen_height = @floatFromInt(raylib.GetScreenHeight());
|
||||||
scales.recalculate();
|
scales.recalculate();
|
||||||
|
column.refresh(false);
|
||||||
}
|
}
|
||||||
if (raylib.IsKeyPressed('D')) debug_draw = !debug_draw;
|
if (raylib.IsKeyPressed('D')) debug_draw = !debug_draw;
|
||||||
if (raylib.IsKeyPressed('S')) raylib.TakeScreenshot("screenshot.png");
|
if (raylib.IsKeyPressed('S')) raylib.TakeScreenshot("screenshot.png");
|
||||||
|
@ -92,43 +93,74 @@ var scales: Scales = undefined;
|
||||||
|
|
||||||
/// Cached scaling and positioning values for dynamic window resizing.
|
/// Cached scaling and positioning values for dynamic window resizing.
|
||||||
pub const Scales = struct {
|
pub const Scales = struct {
|
||||||
item_icon_small_width: f32,
|
font_size_curve: Curve,
|
||||||
item_icon_small_height: f32,
|
icon_size_curve: Curve,
|
||||||
item_icon_large_width: f32,
|
|
||||||
item_icon_large_height: f32,
|
icon_height: f32,
|
||||||
|
icon_width: f32,
|
||||||
|
|
||||||
item_title_font_size: f32,
|
item_title_font_size: f32,
|
||||||
item_subtitle_font_size: f32,
|
item_subtitle_font_size: f32,
|
||||||
|
|
||||||
column_icon_scale: f32,
|
column_item_spacing_curve: Curve,
|
||||||
column_title_font_size: f32,
|
column_title_font_size: f32,
|
||||||
column_position_center: raylib.Vector2,
|
column_position_center: raylib.Vector2,
|
||||||
column_position_spacing: f32,
|
column_position_spacing: f32,
|
||||||
column_item_spacing: f32,
|
column_item_spacing: f32,
|
||||||
column_item_start: f32,
|
column_item_after_start: f32,
|
||||||
column_item_before_start: f32,
|
column_item_before_start: f32,
|
||||||
|
|
||||||
|
pub fn init() Scales {
|
||||||
|
var self: Scales = undefined;
|
||||||
|
|
||||||
|
self.font_size_curve = Curve{ .points = &[_]Curve.Point{
|
||||||
|
.{ .x = 0, .y = 18 },
|
||||||
|
.{ .x = 1, .y = 26 },
|
||||||
|
} };
|
||||||
|
|
||||||
|
self.icon_size_curve = Curve{ .points = &[_]Curve.Point{
|
||||||
|
.{ .x = 0, .y = 48 },
|
||||||
|
.{ .x = 1, .y = 80 },
|
||||||
|
} };
|
||||||
|
|
||||||
|
self.column_item_spacing_curve = Curve{ .points = &[_]Curve.Point{
|
||||||
|
.{ .x = 0, .y = 16, .right_tangent = -90 },
|
||||||
|
.{ .x = 0.25, .y = 0 },
|
||||||
|
.{ .x = 1, .y = 0 },
|
||||||
|
} };
|
||||||
|
|
||||||
|
self.recalculate();
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
/// Recalculate scales after screen resize.
|
/// Recalculate scales after screen resize.
|
||||||
pub fn recalculate(self: *Scales) void {
|
pub fn recalculate(self: *Scales) void {
|
||||||
self.item_icon_small_width = 67;
|
const height = raylib.Remap(screen_height, 272, 1080, 0, 1);
|
||||||
self.item_icon_small_height = 48;
|
const font_size = self.font_size_curve.sample(height);
|
||||||
self.item_icon_large_width = 67;
|
const icon_size = self.icon_size_curve.sample(height);
|
||||||
self.item_icon_large_height = 48;
|
|
||||||
self.item_title_font_size = 18;
|
|
||||||
self.item_subtitle_font_size = 12;
|
|
||||||
|
|
||||||
self.column_icon_scale = 0.75;
|
self.icon_height = icon_size;
|
||||||
self.column_title_font_size = 13;
|
self.icon_width = icon_size * 1.395833;
|
||||||
|
|
||||||
|
self.item_title_font_size = font_size;
|
||||||
|
self.item_subtitle_font_size = font_size * 0.666667;
|
||||||
|
|
||||||
|
self.column_title_font_size = font_size * 0.722222;
|
||||||
self.column_position_center = .{
|
self.column_position_center = .{
|
||||||
.x = std.math.lerp(0.0, screen_width, 0.18),
|
.x = std.math.lerp(0.0, screen_width, 0.18),
|
||||||
.y = std.math.lerp(0.0, screen_height, 0.15),
|
.y = std.math.lerp(0.0, screen_height, 0.15),
|
||||||
};
|
};
|
||||||
self.column_position_spacing = 64;
|
self.column_position_spacing = 64;
|
||||||
self.column_item_spacing = 16;
|
self.column_item_spacing = self.column_item_spacing_curve.sample(height);
|
||||||
self.column_item_start = 117.8;
|
self.column_item_after_start = self.column_position_center.y + self.icon_height + self.column_title_font_size + 12;
|
||||||
self.column_item_before_start = 48;
|
self.column_item_before_start = self.column_position_center.y;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO selected item scale up
|
||||||
|
// TODO item actions
|
||||||
|
// TODO item groups
|
||||||
|
// TODO item group sort
|
||||||
pub const Column = struct {
|
pub const Column = struct {
|
||||||
icon: raylib.Texture2D,
|
icon: raylib.Texture2D,
|
||||||
title: []const u8,
|
title: []const u8,
|
||||||
|
@ -155,8 +187,8 @@ pub const Column = struct {
|
||||||
.box = .{
|
.box = .{
|
||||||
.x = scales.column_position_center.x,
|
.x = scales.column_position_center.x,
|
||||||
.y = scales.column_position_center.y,
|
.y = scales.column_position_center.y,
|
||||||
.w = 67,
|
.w = scales.icon_width,
|
||||||
.h = 48,
|
.h = scales.icon_height,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -173,7 +205,6 @@ pub const Column = struct {
|
||||||
.align_h = .center,
|
.align_h = .center,
|
||||||
};
|
};
|
||||||
|
|
||||||
// self.start_y = scales.column_position_center.y + icon.box.h + title.box.h + scales.column_item_spacing;
|
|
||||||
for (self.items.items) |item| item.update();
|
for (self.items.items) |item| item.update();
|
||||||
for (self.items.items) |item| item.draw();
|
for (self.items.items) |item| item.draw();
|
||||||
|
|
||||||
|
@ -206,19 +237,21 @@ pub const Column = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh(self: *Column, comptime animate: bool) void {
|
fn refresh(self: *Column, comptime animate: bool) void {
|
||||||
var y = scales.column_item_before_start - (scales.item_icon_small_height + scales.column_item_spacing) * @as(f32, @floatFromInt(self.selected));
|
var y = scales.column_item_before_start - (scales.icon_height + scales.column_item_spacing) * @as(f32, @floatFromInt(self.selected));
|
||||||
for (self.items.items[0..self.selected]) |item| {
|
for (self.items.items[0..self.selected]) |item| {
|
||||||
item.setPosition(animate, .{ .x = scales.column_position_center.x, .y = y });
|
item.setPosition(animate, .{ .x = scales.column_position_center.x, .y = y });
|
||||||
y += scales.item_icon_small_height + scales.column_item_spacing;
|
y += scales.icon_height + scales.column_item_spacing;
|
||||||
}
|
}
|
||||||
y = scales.column_item_start;
|
y = scales.column_item_after_start;
|
||||||
for (self.items.items[self.selected..]) |item| {
|
for (self.items.items[self.selected..]) |item| {
|
||||||
item.setPosition(animate, .{ .x = scales.column_position_center.x, .y = y });
|
item.setPosition(animate, .{ .x = scales.column_position_center.x, .y = y });
|
||||||
y += scales.item_icon_small_height + scales.column_item_spacing;
|
y += scales.icon_height + scales.column_item_spacing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO animated item icon scale
|
||||||
|
// TODO animated text fade in/out
|
||||||
pub const Item = struct {
|
pub const Item = struct {
|
||||||
position: raylib.Vector2 = .{ .x = 0, .y = 0 },
|
position: raylib.Vector2 = .{ .x = 0, .y = 0 },
|
||||||
animator: Animator,
|
animator: Animator,
|
||||||
|
@ -226,7 +259,6 @@ pub const Item = struct {
|
||||||
icon: raylib.Texture2D,
|
icon: raylib.Texture2D,
|
||||||
title: []const u8,
|
title: []const u8,
|
||||||
subtitle: []const u8,
|
subtitle: []const u8,
|
||||||
large: bool = false,
|
|
||||||
|
|
||||||
pub fn init(texture: raylib.Texture2D, title: []const u8, subtitle: []const u8) Item {
|
pub fn init(texture: raylib.Texture2D, title: []const u8, subtitle: []const u8) Item {
|
||||||
raylib.SetTextureFilter(texture, raylib.TEXTURE_FILTER_BILINEAR);
|
raylib.SetTextureFilter(texture, raylib.TEXTURE_FILTER_BILINEAR);
|
||||||
|
@ -245,10 +277,10 @@ pub const Item = struct {
|
||||||
var icon = Image{
|
var icon = Image{
|
||||||
.texture = self.icon,
|
.texture = self.icon,
|
||||||
.box = .{
|
.box = .{
|
||||||
.x = self.position.x - scales.item_icon_small_width / 2.0 + 67.0 / 2.0,
|
.x = self.position.x - scales.icon_width / 2.0 + 67.0 / 2.0,
|
||||||
.y = self.position.y,
|
.y = self.position.y,
|
||||||
.w = scales.item_icon_small_width,
|
.w = scales.icon_width,
|
||||||
.h = scales.item_icon_small_height,
|
.h = scales.icon_height,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -521,6 +553,141 @@ fn createCamera() raylib.Camera3D {
|
||||||
return camera;
|
return camera;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Based on parts of code from the MIT-licensed Godot Engine.
|
||||||
|
// https://github.com/godotengine/godot/blob/34b485d62b0a71e85a56fb46cf7ecc59963ed2d6/scene/resources/curve.cpp
|
||||||
|
//
|
||||||
|
// Copyright (c) 2014-present Godot Engine contributors.
|
||||||
|
// Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all
|
||||||
|
// copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
// SOFTWARE.
|
||||||
|
//
|
||||||
|
// -- Godot Engine <https://godotengine.org>
|
||||||
|
pub const Curve = struct {
|
||||||
|
points: []const Point,
|
||||||
|
|
||||||
|
pub fn sample(self: *Curve, x: f32) f32 {
|
||||||
|
if (self.points.len == 0) return 0;
|
||||||
|
if (self.points.len == 1) return self.points[0].y;
|
||||||
|
|
||||||
|
const i = self.getIndex(x);
|
||||||
|
if (i == self.points.len - 1) return self.points[i].y;
|
||||||
|
|
||||||
|
var local = x - self.points[i].x;
|
||||||
|
if (i == 0 and local <= 0) return self.points[0].y;
|
||||||
|
|
||||||
|
const a = self.points[i];
|
||||||
|
const b = self.points[i + 1];
|
||||||
|
|
||||||
|
var d = b.x - a.x;
|
||||||
|
if (std.math.approxEqRel(f32, 0, d, @sqrt(std.math.floatEps(f32))))
|
||||||
|
return b.y;
|
||||||
|
local /= d;
|
||||||
|
d /= 3.0;
|
||||||
|
const yac = a.y + d * a.right_tangent;
|
||||||
|
const ybc = b.y - d * b.left_tangent;
|
||||||
|
|
||||||
|
return bezierInterpolate(a.y, yac, ybc, b.y, local);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getIndex(self: *Curve, x: f32) usize {
|
||||||
|
var imin: usize = 0;
|
||||||
|
var imax = self.points.len - 1;
|
||||||
|
|
||||||
|
while (imax - imin > 1) {
|
||||||
|
const m = (imin + imax) / 2;
|
||||||
|
|
||||||
|
const a = self.points[m].x;
|
||||||
|
const b = self.points[m + 1].x;
|
||||||
|
|
||||||
|
if (a < x and b < x)
|
||||||
|
imin = m
|
||||||
|
else if (a > x)
|
||||||
|
imax = m
|
||||||
|
else
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x > self.points[imax].x) return imax;
|
||||||
|
return imin;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bezierInterpolate(start: f32, control1: f32, control2: f32, end: f32, t: f32) f32 {
|
||||||
|
const omt = 1.0 - t;
|
||||||
|
const omt2 = omt * omt;
|
||||||
|
const omt3 = omt2 * omt;
|
||||||
|
const t2 = t * t;
|
||||||
|
const t3 = t2 * t;
|
||||||
|
|
||||||
|
return start * omt3 + control1 * omt2 * t * 3.0 + control2 * omt * t2 * 3.0 + end * t3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO use this
|
||||||
|
fn updateAutoTangents(self: *Curve, idx: usize) void {
|
||||||
|
var p = &self.points[idx];
|
||||||
|
|
||||||
|
if (idx > 0) {
|
||||||
|
if (p.left_mode == .linear) {
|
||||||
|
const v = raylib.Vector2Normalize(.{
|
||||||
|
.x = self.points[idx - 1].x - p.x,
|
||||||
|
.y = self.points[idx - 1].y - p.y,
|
||||||
|
});
|
||||||
|
p.left_tangent = v.y / v.x;
|
||||||
|
}
|
||||||
|
if (self.points[idx - 1].right_mode == .linear) {
|
||||||
|
const v = raylib.Vector2Normalize(.{
|
||||||
|
.x = self.points[idx - 1].x - p.x,
|
||||||
|
.y = self.points[idx - 1].y - p.y,
|
||||||
|
});
|
||||||
|
self.points[idx - 1].right_tangent = v.y / v.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx + 1 < self.points.len) {
|
||||||
|
if (p.right_mode == .linear) {
|
||||||
|
const v = raylib.Vector2Normalize(.{
|
||||||
|
.x = self.points[idx + 1].x - p.x,
|
||||||
|
.y = self.points[idx + 1].y - p.y,
|
||||||
|
});
|
||||||
|
p.right_tangent = v.y / v.x;
|
||||||
|
}
|
||||||
|
if (self.points[idx + 1].left_mode == .linear) {
|
||||||
|
const v = raylib.Vector2Normalize(.{
|
||||||
|
.x = self.points[idx + 1].x - p.x,
|
||||||
|
.y = self.points[idx + 1].y - p.y,
|
||||||
|
});
|
||||||
|
self.points[idx + 1].right_tangent = v.y / v.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Point = struct {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
left_tangent: f32 = 0.0,
|
||||||
|
right_tangent: f32 = 0.0,
|
||||||
|
left_mode: TangentMode = .free,
|
||||||
|
right_mode: TangentMode = .free,
|
||||||
|
|
||||||
|
pub const TangentMode = enum { free, linear };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue