Misc + made a more controlled digital zoom

Trying to get digital zoom to closer target levels that would match specific even font sizes

Various other changes from iterating on VEFontCache
This commit is contained in:
Edward R. Gonzalez 2024-06-29 22:36:22 -04:00
parent a9ddd7668f
commit 597c88c6b7
14 changed files with 4960 additions and 71 deletions

View File

@ -55,7 +55,7 @@ ui_screen_menu_bar :: proc( captures : rawptr = nil ) -> (should_raise : b32 = f
flags = {},
anchor = range2({},{}),
// alignment = UI_Align_Presets.text_centered,
text_alignment = {0.0, 1.5},
text_alignment = {0.0, 0},
font_size = 12,
margins = {0, 0, 0, 0},
padding = {0, 0, 0, 0},
@ -225,7 +225,7 @@ ui_screen_settings_menu :: proc( captures : rawptr = nil ) -> ( should_raise : b
using title
layout.anchor.ratio.x = 1.0
layout.margins.left = 10
layout.text_alignment = {0, 0.5}
layout.text_alignment = {0, 0.0}
}
input_box := ui_widget("settings_menu.engine_refresh.input_box", {.Mouse_Clickable, .Focusable, .Click_To_Focus}); {

View File

@ -151,10 +151,10 @@ AppConfig :: struct {
cam_zoom_smooth_snappiness : f32,
cam_zoom_sensitivity_smooth : f32,
cam_zoom_sensitivity_digital : f32,
cam_zoom_scroll_delta_scale : f32,
engine_refresh_hz : uint,
timing_fps_moving_avg_alpha : f32,
ui_resize_border_width : f32,

View File

@ -69,7 +69,7 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
// Setup Persistent Slabs & String Cache
{
// alignment := uint(mem.DEFAULT_ALIGNMENT)
alignment := uint(16)
alignment := uint(64)
policy_ptr := & default_slab_policy
push( policy_ptr, SlabSizeClass { 128 * Kilobyte, 1 * Kilobyte, alignment })
@ -88,9 +88,9 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
push( policy_ptr, SlabSizeClass { 16 * Megabyte, 16 * Megabyte, alignment })
push( policy_ptr, SlabSizeClass { 32 * Megabyte, 32 * Megabyte, alignment })
push( policy_ptr, SlabSizeClass { 64 * Megabyte, 64 * Megabyte, alignment })
// push( policy_ptr, SlabSizeClass { 128 * Megabyte, 128 * Megabyte, alignment })
// push( policy_ptr, SlabSizeClass { 256 * Megabyte, 256 * Megabyte, alignment })
// push( policy_ptr, SlabSizeClass { 512 * Megabyte, 512 * Megabyte, alignment })
push( policy_ptr, SlabSizeClass { 128 * Megabyte, 128 * Megabyte, alignment })
push( policy_ptr, SlabSizeClass { 256 * Megabyte, 256 * Megabyte, alignment })
push( policy_ptr, SlabSizeClass { 512 * Megabyte, 512 * Megabyte, alignment })
alloc_error : AllocatorError
persistent_slab, alloc_error = slab_init( policy_ptr, allocator = persistent_allocator(), dbg_name = Persistent_Slab_DBG_Name, enable_mem_tracking = false )
@ -137,11 +137,12 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
resolution_height = 600
refresh_rate = 0
cam_min_zoom = 0.10
cam_min_zoom = 0.025
cam_max_zoom = 5.0
cam_zoom_mode = .Smooth
cam_zoom_mode = .Digital
cam_zoom_smooth_snappiness = 4.0
cam_zoom_sensitivity_digital = 0.05
cam_zoom_sensitivity_digital = 0.25
cam_zoom_scroll_delta_scale = 0.25
cam_zoom_sensitivity_smooth = 2.0
engine_refresh_hz = 0
@ -252,7 +253,10 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
// Setup sokol_gp
{
desc := sokol_gp.Desc {}
desc := sokol_gp.Desc {
max_vertices = 2 * Mega + 640 * Kilo,
max_commands = 1 * Mega,
}
sokol_gp.setup(desc)
verify( cast(b32) sokol_gp.is_valid(), "Failed to setup sokol gp (graphics painter)" )
}
@ -329,7 +333,9 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
}
// debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/Lorem Ipsum (197).txt", allocator = persistent_slab_allocator())
debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/Lorem Ipsum (1022).txt", allocator = persistent_slab_allocator())
// debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/Lorem Ipsum (1022).txt", allocator = persistent_slab_allocator())
// debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/sokol_gp.h", allocator = persistent_slab_allocator())
debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/ve_fontcache.h", allocator = persistent_slab_allocator())
alloc_error : AllocatorError; success : bool
debug.lorem_content, success = os.read_entire_file( debug.path_lorem, persistent_slab_allocator() )
@ -502,8 +508,8 @@ tick_work_frame :: #force_inline proc( host_delta_time_ms : f64 ) -> b32
// rl.PollInputEvents()
debug.draw_ui_box_bounds_points = false
debug.draw_ui_padding_bounds = false
debug.draw_ui_content_bounds = false
debug.draw_ui_padding_bounds = false
debug.draw_ui_content_bounds = false
// config.engine_refresh_hz = 165

View File

@ -258,7 +258,7 @@ render_mode_screenspace :: proc( screen_extent : Extents2, screen_ui : ^UI_State
}
if true {
state.config.font_size_canvas_scalar = 2
state.config.font_size_canvas_scalar = 1.5
zoom_adjust_size := 16 * state.project.workspace.cam.zoom
over_sample := f32(state.config.font_size_canvas_scalar)
debug_text("font_size_canvas_scalar: %v", config.font_size_canvas_scalar)

View File

@ -153,6 +153,65 @@ update :: proc( delta_time : f64 ) -> b32
//region 2D Camera Manual Nav
// TODO(Ed): This should be per workspace view
{
Digial_Zoom_Snap_Levels := []f32{
0.025, // 0.4px (not practical for text, but allows extreme zoom out)
0.03125, // 0.5px
0.0375, // 0.6px
0.04375, // 0.7px
0.05, // 0.8px
0.05625, // 0.9px
0.0625, // 1px
0.075, // 1.2px
0.0875, // 1.4px
0.1, // 1.6px
0.1125, // 1.8px
0.125, // 2px (first practical font size)
0.15, //
0.20, //
0.25, // 4px
0.375, // 6px
0.5, // 8px
0.625, // 10px
0.75, // 12px
0.875, // 14px
1.0, // 16px (base size)
1.125, // 18px
1.25, // 20px
1.375, // 22px
1.5, // 24px
1.625, // 26px
1.75, // 28px
1.875, // 30px
2.0, // 32px
2.125, // 34px
2.25, // 36px
2.375, // 38px
2.5, // 40px
2.625, // 42px
2.75, // 44px
2.875, // 46px
3.0, // 48px
3.125, // 50px
3.25, // 52px
3.375, // 54px
3.5, // 56px
3.625, // 58px
3.75, // 60px
3.875, // 62px
4.0, // 64px
4.125, // 66px
4.25, // 68px
4.375, // 70px
4.5, // 72px
4.625, // 74px
4.75, // 76px
4.875, // 78px
5.0, // 80px
}
Min_Zoom := Digial_Zoom_Snap_Levels[ 0 ]
Max_zoom := Digial_Zoom_Snap_Levels[ len(Digial_Zoom_Snap_Levels) - 1 ]
// profile("Camera Manual Nav")
digital_move_speed : f32 = 1000.0
@ -160,11 +219,30 @@ update :: proc( delta_time : f64 ) -> b32
workspace.zoom_target = cam.zoom
}
// config.cam_max_zoom = 10
// config.cam_min_zoom = 0.05
// config.cam_zoom_sensitivity_digital = 0.05
// config.cam_zoom_sensitivity_smooth = 2.0
// config.cam_zoom_mode = .Smooth
binary_search_closest :: proc(arr: []f32, target: f32) -> int
{
low, high := 0, len(arr) - 1
for low <= high {
mid := (low + high) / 2
if arr[ mid ] == target do return mid
else if arr[ mid ] < target do low = mid + 1
else do high = mid - 1
}
if low == 0 do return 0
if low == len(arr) do return len(arr) - 1
if abs(arr[low-1] - target) < abs(arr[low] - target) {
return low - 1
}
return low
}
find_closest_zoom_index :: proc(zoom: f32, levels : []f32) -> int {
return clamp(binary_search_closest(levels, zoom), 0, len(levels) - 1)
}
switch config.cam_zoom_mode
{
case .Smooth:
@ -177,9 +255,26 @@ update :: proc( delta_time : f64 ) -> b32
cam.zoom += (workspace.zoom_target - cam.zoom) * lerp_factor * f32(delta_time)
cam.zoom = clamp(cam.zoom, config.cam_min_zoom, config.cam_max_zoom) // Ensure cam.zoom stays within bounds
case .Digital:
zoom_delta := clamp(input.mouse.scroll.y, -1, 1) * config.cam_zoom_sensitivity_digital
workspace.zoom_target = clamp(workspace.zoom_target + zoom_delta, config.cam_min_zoom, config.cam_max_zoom)
cam.zoom = workspace.zoom_target
zoom_delta := input.mouse.scroll.y
if zoom_delta != 0 {
current_index := find_closest_zoom_index(cam.zoom, Digial_Zoom_Snap_Levels)
scroll_speed := max(1, abs(zoom_delta) * config.cam_zoom_scroll_delta_scale) // Adjust this factor to control sensitivity
target_index := current_index
if zoom_delta > 0 {
target_index = min(len(Digial_Zoom_Snap_Levels) - 1, current_index + int(scroll_speed))
} else if zoom_delta < 0 {
target_index = max(0, current_index - int(scroll_speed))
}
if target_index != current_index {
workspace.zoom_target = Digial_Zoom_Snap_Levels[target_index]
}
}
// Smooth transition to target zoom
cam.zoom = lerp(cam.zoom, workspace.zoom_target, cast(f32) config.cam_zoom_sensitivity_digital)
}
move_velocity : Vec2 = {

View File

@ -51,6 +51,7 @@ import fmt_io "core:fmt"
str_tmp_from_any :: fmt_io.tprint
import "core:math"
lerp :: math.lerp
import "core:math/bits"
u64_max :: bits.U64_MAX

View File

@ -205,7 +205,8 @@ render_to_ws_view_pos :: #force_inline proc "contextless" (pos : Vec2) -> Vec2 {
screen_to_ws_view_pos :: #force_inline proc "contextless" (pos: Vec2) -> Vec2 {
state := get_state(); using state
cam := & project.workspace.cam
result := pos - cam.position * cam.zoom
cam_zoom_ratio := 1.0 / cam.zoom
result := pos * cam_zoom_ratio - cam.position
return result
}
@ -227,8 +228,17 @@ ws_view_extent :: #force_inline proc "contextless"() -> Extents2 {
// Workspace view to screen space position
// TODO(Ed): Support a position which would not be centered on the screen if in a viewport
ws_view_to_screen_pos :: #force_inline proc "contextless"(position: Vec2) -> Vec2 {
return position
ws_view_to_screen_pos :: proc(ws_pos : Vec2) -> Vec2 {
cam := &get_state().project.workspace.cam
screen_extent := get_state().app_window.extent
// Apply camera transformation
view_pos := (ws_pos - cam.position) * cam.zoom
// Convert to screen space
screen_pos := view_pos
return screen_pos
}
ws_view_to_render_pos :: #force_inline proc "contextless"(position: Vec2) -> Vec2 {

View File

@ -504,15 +504,16 @@ ui_top_ancestor :: #force_inline proc "contextless" ( box : ^UI_Box ) -> (^UI_Bo
return ancestor
}
ui_view_bounds :: #force_inline proc "contextless" () -> (range : Range2) {
using state := get_state();
// if ui_context == & screen_ui {
// return screen_get_bounds()
// }
// else {
ui_view_bounds :: #force_inline proc "contextless" ( ui : ^UI_State = nil ) -> (range : Range2) {
state := get_state(); using state
ui := ui; if ui == nil do ui = ui_context
if ui == & screen_ui {
return screen_get_bounds()
}
else {
return view_get_bounds()
// }
}
}
ui_context :: #force_inline proc "contextless" () -> ^UI_State { return get_state().ui_context }

View File

@ -118,36 +118,33 @@ ui_prev_cached_box :: #force_inline proc( box : ^UI_Box ) -> ^UI_Box { return hm
// TODO(Ed): Rename to ui_box_tranverse_view_next
// Traveral pritorizes immeidate children
ui_box_tranverse_next_depth_first :: #force_inline proc "contextless" ( box : ^ UI_Box, bypass_intersection_test := false ) -> (^ UI_Box)
{
using state := get_state()
// If current has children, do them first
if box.first != nil
{
// Check to make sure parent is present on the screen, if its not don't bother.
if bypass_intersection_test {
return box.first
}
if intersects_range2( ui_view_bounds(), box.computed.bounds) {
return box.first
ui_box_tranverse_next_depth_first :: #force_inline proc "contextless" (box: ^UI_Box, bypass_intersection_test := false, ctx: ^UI_State = nil) -> ^UI_Box {
state := get_state(); using state
ctx := ctx if ctx != nil else ui_context
// If current has children, check if we should traverse them
if box.first != nil {
if bypass_intersection_test || intersects_range2(ui_view_bounds(ctx), box.computed.bounds) {
return box.first
}
}
if box.next != nil do return box.next
// There are no more adjacent nodes
// If no children or children are culled, try next sibling
if box.next != nil {
return box.next
}
// No more siblings, traverse up the tree
parent := box.parent
// Attempt to find a parent with a next, otherwise we just return a parent with nil
for ; parent.parent != nil;
{
if parent.next != nil {
break
}
parent = parent.parent
for parent != nil {
if parent.next != nil {
return parent.next
}
parent = parent.parent
}
// Lift back up to parent, and set it to its next.
return parent.next
// We've reached the end of the tree
return nil
}
// Traveral pritorizes traversing a "anestry layer"

View File

@ -166,9 +166,11 @@ ui_box_compute_layout :: proc( box : ^UI_Box,
// 8. Text position & size
if len(box.text.str) > 0
{
ascent, descent, line_gap := get_font_vertical_metrics(style.font, layout.font_size)
content_size := content_bounds.max - content_bounds.min
text_pos : Vec2
text_pos = content_bounds.min + { 0, text_size.y * 0.5 }
text_pos = content_bounds.min
text_pos += { 0, -descent }
text_pos += (content_size - text_size) * layout.text_alignment
computed.text_size = text_size

View File

@ -163,17 +163,17 @@ test_whitespace_ast :: proc( default_layout : ^UI_Layout, frame_style_default :
text_layout.flags = {
// .Origin_At_Anchor_Center,
.Fixed_Position_X, .Fixed_Position_Y,
.Fixed_Width, .Fixed_Height,
.Fixed_Width, .Fixed_Height,
}
text_layout.text_alignment = { 0.0, 0.5 }
text_layout.alignment = { 0.0, 0.0 }
text_layout.size.min = { 1600, 20 }
text_layout.alignment = { 0.0, 1.0 }
text_layout.size.min = { 1600, 14 }
text_style := frame_style_default ^
text_style_combo := to_ui_style_combo(text_style)
text_style_combo.default.bg_color = Color_Transparent
text_style_combo.disabled.bg_color = Color_Frame_Disabled
text_style_combo.hot.bg_color = Color_Frame_Hover
text_style_combo.active.bg_color = Color_Frame_Select
text_style_combo.disabled.bg_color = Color_Transparent
text_style_combo.hot.bg_color = Color_Transparent
text_style_combo.active.bg_color = Color_Transparent
scope( text_layout, text_style )
alloc_error : AllocatorError; success : bool
@ -204,14 +204,14 @@ test_whitespace_ast :: proc( default_layout : ^UI_Layout, frame_style_default :
ui_layout( text_layout )
line_hbox := ui_widget(str_fmt( "line %v", line_id ), {.Mouse_Clickable})
if line_hbox.key == ui.hot
if line_hbox.key == ui.hot && false
{
line_hbox.text = StrRunesPair {}
ui_parent(line_hbox)
chunk_layout := text_layout
chunk_layout.alignment = { 0.0, 0.5 }
chunk_layout.anchor = range2({ 0.0, 0 }, { 0.0, 0 })
chunk_layout.alignment = { 0.0, 0.0 }
chunk_layout.anchor = range2({ 0.0, 0.0 }, { 0.0, 0.0 })
chunk_layout.pos = {}
chunk_layout.flags = { .Fixed_Position_X, .Size_To_Text }
@ -286,12 +286,12 @@ test_whitespace_ast :: proc( default_layout : ^UI_Layout, frame_style_default :
if len(line_hbox.text.str) > 0 {
array_append( widgets_ptr, line_hbox )
text_layout.pos.x = text_layout.pos.x
text_layout.pos.y += size_range2(line_hbox.computed.bounds).y - 8
text_layout.pos.y += size_range2(line_hbox.computed.bounds).y
}
else {
widget := & widgets.data[ widgets.num - 1 ]
if widget.box != nil {
text_layout.pos.y += size_range2( widget.computed.bounds ).y - 8
text_layout.pos.y += size_range2( widget.computed.bounds ).y
}
}

2978
examples/sokol_gp.h Normal file

File diff suppressed because it is too large Load Diff

1799
examples/ve_fontcache.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -204,10 +204,10 @@ push-location $path_root
# $build_args += $flag_micro_architecture_native
$build_args += $flag_use_separate_modules
$build_args += $flag_thread_count + $CoreCount_Physical
$build_args += $flag_optimize_none
# $build_args += $flag_optimize_none
# $build_args += $flag_optimize_minimal
# $build_args += $flag_optimize_speed
# $build_args += $falg_optimize_aggressive
$build_args += $falg_optimize_aggressive
$build_args += $flag_debug
$build_args += $flag_pdb_name + $pdb
$build_args += $flag_subsystem + 'windows'