This commit is contained in:
2026-01-21 09:05:02 -06:00
parent 18ca9e14ba
commit f7be9c3344
30 changed files with 237 additions and 246 deletions

View File

@@ -226,7 +226,7 @@ action.on_input = function(action_id, evt)
var matched_actions = [] var matched_actions = []
arrfor(array(this.action_map), mapped_action => { arrfor(array(this.action_map), mapped_action => {
if (find(this.action_map[mapped_action], action_id) != null) { if (find(this.action_map[mapped_action], action_id) != null) {
matched_actions.push(mapped_action) push(matched_actions, mapped_action)
if (evt.pressed) if (evt.pressed)
this.down[mapped_action] = true this.down[mapped_action] = true
@@ -256,14 +256,14 @@ action.rebind_action = function(action_name, new_key) {
arrfor(array(this.action_map), act => { arrfor(array(this.action_map), act => {
var idx = find(this.action_map[act], new_key) var idx = find(this.action_map[act], new_key)
if (idx != null) if (idx != null)
this.action_map[act].splice(idx, 1) this.action_map[act] = array(array(this.action_map[act], 0, idx), array(this.action_map[act], idx+1))
}) })
// Clear existing bindings for the current device from the target action // Clear existing bindings for the current device from the target action
var target_bindings = this.action_map[action_name] var target_bindings = this.action_map[action_name]
for (var i = length(target_bindings) - 1; i >= 0; i--) { for (var i = length(target_bindings) - 1; i >= 0; i--) {
if (detect_device(target_bindings[i]) == this.current_device) if (detect_device(target_bindings[i]) == this.current_device)
target_bindings.splice(i, 1) this.action_map[action_name] = array(array(this.action_map[action_name], 0, i), array(this.action_map[action_name], i+1))
} }
// Only insert into the target if it's the right device // Only insert into the target if it's the right device

16
clay.cm
View File

@@ -130,7 +130,7 @@ function build_drawables(node, root_height, parent_abs_x, parent_abs_y, parent_s
// Background // Background
if (node.config.background_image) { if (node.config.background_image) {
if (node.config.slice) { if (node.config.slice) {
drawables.push({ push(drawables, {
type: 'sprite', type: 'sprite',
image: node.config.background_image, image: node.config.background_image,
pos: {x: vis_x, y: vis_y}, pos: {x: vis_x, y: vis_y},
@@ -142,7 +142,7 @@ function build_drawables(node, root_height, parent_abs_x, parent_abs_y, parent_s
scissor: current_scissor scissor: current_scissor
}) })
} else { } else {
drawables.push({ push(drawables, {
type: 'sprite', type: 'sprite',
image: node.config.background_image, image: node.config.background_image,
pos: {x: vis_x, y: vis_y}, pos: {x: vis_x, y: vis_y},
@@ -154,7 +154,7 @@ function build_drawables(node, root_height, parent_abs_x, parent_abs_y, parent_s
}) })
} }
} else if (node.config.background_color) { } else if (node.config.background_color) {
drawables.push({ push(drawables, {
type: 'rect', type: 'rect',
pos: {x: vis_x, y: vis_y}, pos: {x: vis_x, y: vis_y},
width: rect.width, width: rect.width,
@@ -167,7 +167,7 @@ function build_drawables(node, root_height, parent_abs_x, parent_abs_y, parent_s
// Content (Image/Text) // Content (Image/Text)
if (node.config.image) { if (node.config.image) {
drawables.push({ push(drawables, {
type: 'sprite', type: 'sprite',
image: node.config.image, image: node.config.image,
pos: {x: vis_x, y: vis_y}, pos: {x: vis_x, y: vis_y},
@@ -180,7 +180,7 @@ function build_drawables(node, root_height, parent_abs_x, parent_abs_y, parent_s
} }
if (node.config.text) { if (node.config.text) {
drawables.push({ push(drawables, {
type: 'text', type: 'text',
text: node.config.text, text: node.config.text,
font: node.config.font_path, font: node.config.font_path,
@@ -249,15 +249,15 @@ function push_node(configs, contain_mode) {
// Add to parent // Add to parent
var parent = tree_stack[length(tree_stack)-1] var parent = tree_stack[length(tree_stack)-1]
parent.children.push(node) push(parent.children, node)
lay_ctx.insert(parent.id, item) lay_ctx.insert(parent.id, item)
tree_stack.push(node) push(tree_stack, node)
return node return node
} }
function pop_node() { function pop_node() {
tree_stack.pop() pop(tree_stack)
} }
// Generic container // Generic container

View File

@@ -74,7 +74,7 @@ clay_input.click = function click(tree_root, mousepos, button = 'left') {
clay_input.get_actionable = function get_actionable(tree_root) { clay_input.get_actionable = function get_actionable(tree_root) {
var actionable = [] var actionable = []
function walk(node) { function walk(node) {
if (node.config.action) actionable.push(node) if (node.config.action) push(actionable, node)
if (node[clay.CHILDREN]) if (node[clay.CHILDREN])
arrfor(node[clay.CHILDREN], walk) arrfor(node[clay.CHILDREN], walk)
} }
@@ -85,7 +85,7 @@ clay_input.get_actionable = function get_actionable(tree_root) {
clay_input.filter = function filter(tree_root, predicate) { clay_input.filter = function filter(tree_root, predicate) {
var results = [] var results = []
function rec(node) { function rec(node) {
if (predicate(node)) results.push(node) if (predicate(node)) push(results, node)
if (node[clay.CHILDREN]) if (node[clay.CHILDREN])
arrfor(node[clay.CHILDREN], rec) arrfor(node[clay.CHILDREN], rec)
} }

View File

@@ -23,7 +23,7 @@ compositor.compile = function(config) {
// Clear screen // Clear screen
if (config.clear) if (config.clear)
ctx.passes.push({type: 'clear', target: 'screen', color: config.clear}) push(ctx.passes, {type: 'clear', target: 'screen', color: config.clear})
// Process each plane (supports both 'planes' and legacy 'layers' key) // Process each plane (supports both 'planes' and legacy 'layers' key)
var planes = config.planes || config.layers || [] var planes = config.planes || config.layers || []
@@ -41,7 +41,7 @@ compositor.compile = function(config) {
} }
function compile_imgui_layer(layer, ctx) { function compile_imgui_layer(layer, ctx) {
ctx.passes.push({ push(ctx.passes, {
type: 'imgui', type: 'imgui',
target: 'screen', target: 'screen',
draw: layer.draw draw: layer.draw
@@ -70,7 +70,7 @@ function compile_plane(plane_config, ctx, group_effects) {
// Add manual drawables // Add manual drawables
if (plane_config.drawables) { if (plane_config.drawables) {
for (var i = 0; i < length(plane_config.drawables); i++) for (var i = 0; i < length(plane_config.drawables); i++)
all_sprites.push(plane_config.drawables[i]) push(all_sprites, plane_config.drawables[i])
} }
// Find which sprites belong to groups with effects // Find which sprites belong to groups with effects
@@ -98,14 +98,14 @@ function compile_plane(plane_config, ctx, group_effects) {
if (group_effects[gname]) { if (group_effects[gname]) {
if (!effect_groups[gname]) if (!effect_groups[gname])
effect_groups[gname] = {sprites: [], effects: group_effects[gname].effects} effect_groups[gname] = {sprites: [], effects: group_effects[gname].effects}
effect_groups[gname].sprites.push(s) push(effect_groups[gname].sprites, s)
assigned = true assigned = true
break // Only assign to first matching effect group break // Only assign to first matching effect group
} }
} }
// Add to base sprites if not assigned to effect group and not mask-only // Add to base sprites if not assigned to effect group and not mask-only
if (!assigned && !is_mask_only) base_sprites.push(s) if (!assigned && !is_mask_only) push(base_sprites, s)
} }
// Allocate plane target // Allocate plane target
@@ -113,7 +113,7 @@ function compile_plane(plane_config, ctx, group_effects) {
// Clear plane // Clear plane
if (plane_config.clear) if (plane_config.clear)
ctx.passes.push({type: 'clear', target: plane_target, color: plane_config.clear}) push(ctx.passes, {type: 'clear', target: plane_target, color: plane_config.clear})
// Render each effect group to temp target, apply effects, composite back // Render each effect group to temp target, apply effects, composite back
arrfor(array(effect_groups), gname => { arrfor(array(effect_groups), gname => {
@@ -123,7 +123,7 @@ function compile_plane(plane_config, ctx, group_effects) {
var group_target = ctx.alloc(res.width, res.height, gname + '_content') var group_target = ctx.alloc(res.width, res.height, gname + '_content')
// Render group content // Render group content
ctx.passes.push({ push(ctx.passes, {
type: 'render', type: 'render',
renderer: 'film2d', renderer: 'film2d',
drawables: eg.sprites, drawables: eg.sprites,
@@ -142,7 +142,7 @@ function compile_plane(plane_config, ctx, group_effects) {
} }
// Composite result to plane // Composite result to plane
ctx.passes.push({ push(ctx.passes, {
type: 'composite', type: 'composite',
source: current, source: current,
dest: plane_target, dest: plane_target,
@@ -154,7 +154,7 @@ function compile_plane(plane_config, ctx, group_effects) {
// Render base sprites (no effects) // Render base sprites (no effects)
if (length(base_sprites) > 0) { if (length(base_sprites) > 0) {
ctx.passes.push({ push(ctx.passes, {
type: 'render', type: 'render',
renderer: 'film2d', renderer: 'film2d',
drawables: base_sprites, drawables: base_sprites,
@@ -167,7 +167,7 @@ function compile_plane(plane_config, ctx, group_effects) {
} }
// Composite plane to screen // Composite plane to screen
ctx.passes.push({ push(ctx.passes, {
type: 'blit_to_screen', type: 'blit_to_screen',
source: plane_target, source: plane_target,
source_size: res, source_size: res,
@@ -185,7 +185,7 @@ function apply_effect(ctx, effect, input, size, camera, hint, current_plane, gro
var blur2 = ctx.alloc(size.width, size.height, hint + '_blur2') var blur2 = ctx.alloc(size.width, size.height, hint + '_blur2')
// Threshold // Threshold
ctx.passes.push({ push(ctx.passes, {
type: 'shader_pass', type: 'shader_pass',
shader: 'threshold', shader: 'threshold',
input: input, input: input,
@@ -197,13 +197,13 @@ function apply_effect(ctx, effect, input, size, camera, hint, current_plane, gro
var blur_passes = effect.blur_passes || 2 var blur_passes = effect.blur_passes || 2
var blur_in = bright var blur_in = bright
for (var p = 0; p < blur_passes; p++) { for (var p = 0; p < blur_passes; p++) {
ctx.passes.push({type: 'shader_pass', shader: 'blur', input: blur_in, output: blur1, uniforms: {direction: {x: 1, y: 0}, texel_size: {x: 1/size.width, y: 1/size.height}}}) push(ctx.passes, {type: 'shader_pass', shader: 'blur', input: blur_in, output: blur1, uniforms: {direction: {x: 1, y: 0}, texel_size: {x: 1/size.width, y: 1/size.height}}})
ctx.passes.push({type: 'shader_pass', shader: 'blur', input: blur1, output: blur2, uniforms: {direction: {x: 0, y: 1}, texel_size: {x: 1/size.width, y: 1/size.height}}}) push(ctx.passes, {type: 'shader_pass', shader: 'blur', input: blur1, output: blur2, uniforms: {direction: {x: 0, y: 1}, texel_size: {x: 1/size.width, y: 1/size.height}}})
blur_in = blur2 blur_in = blur2
} }
// Composite bloom // Composite bloom
ctx.passes.push({type: 'composite_textures', base: input, overlay: blur2, output: output, mode: 'add'}) push(ctx.passes, {type: 'composite_textures', base: input, overlay: blur2, output: output, mode: 'add'})
} else if (effect.type == 'mask') { } else if (effect.type == 'mask') {
var mask_group = effect.mask_group var mask_group = effect.mask_group
@@ -214,7 +214,7 @@ function apply_effect(ctx, effect, input, size, camera, hint, current_plane, gro
var mask_target = ctx.alloc(size.width, size.height, hint + '_mask') var mask_target = ctx.alloc(size.width, size.height, hint + '_mask')
// Render mask // Render mask
ctx.passes.push({ push(ctx.passes, {
type: 'render', type: 'render',
renderer: 'film2d', renderer: 'film2d',
drawables: mask_sprites, drawables: mask_sprites,
@@ -225,7 +225,7 @@ function apply_effect(ctx, effect, input, size, camera, hint, current_plane, gro
}) })
// Apply mask // Apply mask
ctx.passes.push({ push(ctx.passes, {
type: 'apply_mask', type: 'apply_mask',
content: input, content: input,
mask: mask_target, mask: mask_target,
@@ -235,11 +235,11 @@ function apply_effect(ctx, effect, input, size, camera, hint, current_plane, gro
}) })
} else { } else {
// No mask sprites, pass through // No mask sprites, pass through
ctx.passes.push({type: 'blit', source: input, dest: output}) push(ctx.passes, {type: 'blit', source: input, dest: output})
} }
} else { } else {
// Unknown effect, pass through // Unknown effect, pass through
ctx.passes.push({type: 'blit', source: input, dest: output}) push(ctx.passes, {type: 'blit', source: input, dest: output})
} }
return output return output
@@ -267,8 +267,8 @@ compositor.execute = function(plan) {
if (pass.type == 'clear') { if (pass.type == 'clear') {
var target = resolve(pass.target) var target = resolve(pass.target)
commands.push({cmd: 'begin_render', target: target, clear: pass.color}) push(commands, {cmd: 'begin_render', target: target, clear: pass.color})
commands.push({cmd: 'end_render'}) push(commands, {cmd: 'end_render'})
} else if (pass.type == 'render') { } else if (pass.type == 'render') {
var result = film2d.render({ var result = film2d.render({
@@ -280,10 +280,10 @@ compositor.execute = function(plan) {
clear: pass.clear clear: pass.clear
}, backend) }, backend)
for (var c = 0; c < length(result.commands); c++) for (var c = 0; c < length(result.commands); c++)
commands.push(result.commands[c]) push(commands, result.commands[c])
} else if (pass.type == 'shader_pass') { } else if (pass.type == 'shader_pass') {
commands.push({ push(commands, {
cmd: 'shader_pass', cmd: 'shader_pass',
shader: pass.shader, shader: pass.shader,
input: resolve(pass.input), input: resolve(pass.input),
@@ -292,7 +292,7 @@ compositor.execute = function(plan) {
}) })
} else if (pass.type == 'composite_textures') { } else if (pass.type == 'composite_textures') {
commands.push({ push(commands, {
cmd: 'composite_textures', cmd: 'composite_textures',
base: resolve(pass.base), base: resolve(pass.base),
overlay: resolve(pass.overlay), overlay: resolve(pass.overlay),
@@ -301,7 +301,7 @@ compositor.execute = function(plan) {
}) })
} else if (pass.type == 'apply_mask') { } else if (pass.type == 'apply_mask') {
commands.push({ push(commands, {
cmd: 'apply_mask', cmd: 'apply_mask',
content_texture: resolve(pass.content), content_texture: resolve(pass.content),
mask_texture: resolve(pass.mask), mask_texture: resolve(pass.mask),
@@ -311,7 +311,7 @@ compositor.execute = function(plan) {
}) })
} else if (pass.type == 'composite') { } else if (pass.type == 'composite') {
commands.push({ push(commands, {
cmd: 'blit', cmd: 'blit',
texture: resolve(pass.source), texture: resolve(pass.source),
target: resolve(pass.dest), target: resolve(pass.dest),
@@ -320,7 +320,7 @@ compositor.execute = function(plan) {
} else if (pass.type == 'blit_to_screen') { } else if (pass.type == 'blit_to_screen') {
var rect = _calc_presentation(pass.source_size, pass.dest_size, pass.presentation) var rect = _calc_presentation(pass.source_size, pass.dest_size, pass.presentation)
commands.push({ push(commands, {
cmd: 'blit', cmd: 'blit',
texture: resolve(pass.source), texture: resolve(pass.source),
target: 'screen', target: 'screen',
@@ -330,14 +330,14 @@ compositor.execute = function(plan) {
} else if (pass.type == 'blit') { } else if (pass.type == 'blit') {
var src = resolve(pass.source) var src = resolve(pass.source)
var dst = resolve(pass.dest) var dst = resolve(pass.dest)
commands.push({ push(commands, {
cmd: 'blit', cmd: 'blit',
texture: src, texture: src,
target: dst, target: dst,
dst_rect: {x: 0, y: 0, width: dst.width, height: dst.height} dst_rect: {x: 0, y: 0, width: dst.width, height: dst.height}
}) })
} else if (pass.type == 'imgui') { } else if (pass.type == 'imgui') {
commands.push({ push(commands, {
cmd: 'imgui', cmd: 'imgui',
target: resolve(pass.target), target: resolve(pass.target),
draw: pass.draw draw: pass.draw

40
core.cm
View File

@@ -78,7 +78,29 @@ core.backend = function() {
} }
// FPS tracking // FPS tracking
var _fps_samples = [] var _fps_samples = []
var _fps_sample_count = 60 var _fps_sample_count = 120
var _fps_sample_sum = 0
var _fps_sample_pos = 0
function fps_add_sample(sample) {
var n = length(_fps_samples)
if (n < _fps_sample_count) {
push(_fps_samples, sample)
_fps_sample_sum += sample
} else {
var old = _fps_samples[_fps_sample_pos]
_fps_samples[_fps_sample_pos] = sample
_fps_sample_sum += sample - old
_fps_sample_pos++
if (_fps_sample_pos >= _fps_sample_count) _fps_sample_pos = 0
}
}
function fps_get_avg() {
var n = length(_fps_samples)
return n ? _fps_sample_sum / n : 0
}
var _current_fps = 0 var _current_fps = 0
var _frame_time_ms = 0 var _frame_time_ms = 0
@@ -162,7 +184,7 @@ function _main_loop() {
// Handle both compositor result ({commands: [...]}) and fx_graph (graph object) // Handle both compositor result ({commands: [...]}) and fx_graph (graph object)
if (render_result.commands) { if (render_result.commands) {
if (_config.imgui || _config.editor) { if (_config.imgui || _config.editor) {
render_result.commands.push({ push(render_result.commands, {
cmd: 'imgui', cmd: 'imgui',
draw: function(ui) { draw: function(ui) {
if (_config.imgui) _config.imgui(ui) if (_config.imgui) _config.imgui(ui)
@@ -188,19 +210,15 @@ function _main_loop() {
} }
} }
// Measure actual frame work time (excluding delay)
var frame_end = time_mod.number() var frame_end = time_mod.number()
var actual_frame_time = frame_end - frame_start var actual_frame_time = frame_end - frame_start
// Track FPS based on actual work time
_frame_time_ms = actual_frame_time * 1000 _frame_time_ms = actual_frame_time * 1000
_fps_samples.push(actual_frame_time) fps_add_sample(actual_frame_time)
if (length(_fps_samples) > _fps_sample_count) {
_fps_samples.shift() var avg_frame_time = fps_get_avg()
}
var avg_frame_time = reduce(_fps_samples, function(a,b) { return a+b }) / length(_fps_samples)
_current_fps = avg_frame_time > 0 ? 1 / avg_frame_time : 0 _current_fps = avg_frame_time > 0 ? 1 / avg_frame_time : 0
// Schedule next frame // Schedule next frame
var frame_time = 1 / _framerate var frame_time = 1 / _framerate
var elapsed = frame_end - frame_start var elapsed = frame_end - frame_start

View File

@@ -143,29 +143,29 @@ function _render_node_summary(imgui, node) {
var info = [] var info = []
if (node.pos) { if (node.pos) {
info.push("pos:(" + text(round(node.pos.x)) + "," + text(round(node.pos.y)) + ")") push(info, "pos:(" + text(round(node.pos.x)) + "," + text(round(node.pos.y)) + ")")
} }
if (node.width && node.height) { if (node.width && node.height) {
info.push("size:" + text(node.width) + "x" + text(node.height)) push(info, "size:" + text(node.width) + "x" + text(node.height))
} }
if (node.image) { if (node.image) {
info.push("img:" + node.image) push(info, "img:" + node.image)
} }
if (node.text) { if (node.text) {
var t = node.text var t = node.text
if (length(t) > 20) t = text(t, 0, 17) + "..." if (length(t) > 20) t = text(t, 0, 17) + "..."
info.push("\"" + t + "\"") push(info, "\"" + t + "\"")
} }
if (node.effects && length(node.effects) > 0) { if (node.effects && length(node.effects) > 0) {
var fx = [] var fx = []
for (var i = 0; i < length(node.effects); i++) { for (var i = 0; i < length(node.effects); i++) {
fx.push(node.effects[i].type) push(fx, node.effects[i].type)
} }
info.push("fx:[" + text(fx, ",") + "]") push(info, "fx:[" + text(fx, ",") + "]")
} }
if (length(info) > 0) { if (length(info) > 0) {

View File

@@ -36,7 +36,7 @@ effects.register('bloom', {
// Threshold extraction // Threshold extraction
var thresh_target = ctx.alloc_target(size.width, size.height, 'bloom_thresh') var thresh_target = ctx.alloc_target(size.width, size.height, 'bloom_thresh')
passes.push({ push(passes, {
type: 'shader', type: 'shader',
shader: 'threshold', shader: 'threshold',
input: input, input: input,
@@ -55,14 +55,14 @@ effects.register('bloom', {
var blur_count = params.blur_passes != null ? params.blur_passes : 3 var blur_count = params.blur_passes != null ? params.blur_passes : 3
for (var i = 0; i < blur_count; i++) { for (var i = 0; i < blur_count; i++) {
passes.push({ push(passes, {
type: 'shader', type: 'shader',
shader: 'blur', shader: 'blur',
input: blur_src, input: blur_src,
output: blur_a, output: blur_a,
uniforms: {direction: {x: 2, y: 0}, texel_size: texel} uniforms: {direction: {x: 2, y: 0}, texel_size: texel}
}) })
passes.push({ push(passes, {
type: 'shader', type: 'shader',
shader: 'blur', shader: 'blur',
input: blur_a, input: blur_a,
@@ -73,7 +73,7 @@ effects.register('bloom', {
} }
// Additive composite // Additive composite
passes.push({ push(passes, {
type: 'composite', type: 'composite',
base: input, base: input,
overlay: blur_src, overlay: blur_src,
@@ -124,7 +124,7 @@ effects.register('mask', {
// Render mask source to target // Render mask source to target
var mask_target = ctx.alloc_target(size.width, size.height, 'mask_src') var mask_target = ctx.alloc_target(size.width, size.height, 'mask_src')
passes.push({ push(passes, {
type: 'render_subtree', type: 'render_subtree',
root: mask_source, root: mask_source,
output: mask_target, output: mask_target,
@@ -133,7 +133,7 @@ effects.register('mask', {
}) })
// Apply mask shader // Apply mask shader
passes.push({ push(passes, {
type: 'shader', type: 'shader',
shader: 'mask', shader: 'mask',
inputs: [input, mask_target], inputs: [input, mask_target],
@@ -190,14 +190,14 @@ effects.register('blur', {
var blur_count = params.passes != null ? params.passes : 2 var blur_count = params.passes != null ? params.passes : 2
for (var i = 0; i < blur_count; i++) { for (var i = 0; i < blur_count; i++) {
passes.push({ push(passes, {
type: 'shader', type: 'shader',
shader: 'blur', shader: 'blur',
input: src, input: src,
output: blur_a, output: blur_a,
uniforms: {direction: {x: 2, y: 0}, texel_size: texel} uniforms: {direction: {x: 2, y: 0}, texel_size: texel}
}) })
passes.push({ push(passes, {
type: 'shader', type: 'shader',
shader: 'blur', shader: 'blur',
input: blur_a, input: blur_a,
@@ -208,7 +208,7 @@ effects.register('blur', {
} }
// Final blit to output // Final blit to output
passes.push({type: 'blit', source: src, dest: output}) push(passes, {type: 'blit', source: src, dest: output})
return passes return passes
} }
}) })

View File

@@ -14,7 +14,7 @@ var bunnies = []
// Start with some initial bunnies: // Start with some initial bunnies:
for (var i = 0; i < 100; i++) { for (var i = 0; i < 100; i++) {
bunnies.push({ push(bunnies, {
x: random.random() * config.width, x: random.random() * config.width,
y: random.random() * config.height, y: random.random() * config.height,
vx: (random.random() * 300) - 150, vx: (random.random() * 300) - 150,
@@ -22,22 +22,12 @@ for (var i = 0; i < 100; i++) {
}) })
} }
var fpsSamples = []
var fpsAvg = 0
this.update = function(dt) { this.update = function(dt) {
// Compute FPS average over the last 60 frames:
var currentFPS = 1 / dt
fpsSamples.push(currentFPS)
if (length(fpsSamples) > 60) fpsSamples.shift()
var sum = reduce(fpsSamples, function(a,b) { return a + b })
fpsAvg = sum / length(fpsSamples)
// If left mouse is down, spawn some more bunnies: // If left mouse is down, spawn some more bunnies:
var mouse = input.mousestate() var mouse = input.mousestate()
if (mouse.left) if (mouse.left)
for (var i = 0; i < 50; i++) { for (var i = 0; i < 50; i++) {
bunnies.push({ push(bunnies, {
x: mouse.x, x: mouse.x,
y: mouse.y, y: mouse.y,
vx: (random.random() * 300) - 150, vx: (random.random() * 300) - 150,
@@ -63,7 +53,4 @@ this.update = function(dt) {
this.hud = function() { this.hud = function() {
draw.images(bunnyTex, bunnies) draw.images(bunnyTex, bunnies)
var msg = 'FPS: ' + fpsAvg.toFixed(2) + ' Bunnies: ' + length(bunnies)
draw.text(msg, {x:0, y:0, width:config.width, height:40}, null, 0, color.white, 0)
} }

View File

@@ -26,15 +26,13 @@ var grid_prototype = {
// add an entity into a cell // add an entity into a cell
add(entity, pos) { add(entity, pos) {
this.cell(pos.x, pos.y).push(entity); push(this.cell(pos.x, pos.y), entity);
entity.coord = array(pos); entity.coord = array(pos);
}, },
// remove an entity from a cell // remove an entity from a cell
remove(entity, pos) { remove(entity, pos) {
def c = this.cell(pos.x, pos.y); this.cells[pos.y][pos.x] = filter(this.cells[pos.y][pos.x], x => x != entity)
def i = search(c, entity);
if (i != null) c.splice(i, 1);
}, },
// bounds check // bounds check

View File

@@ -1,28 +1,27 @@
/* pieces.js simple data holders + starting layout */ /* pieces.js simple data holders + starting layout */
function Piece(kind, colour) { function Piece(kind, colour) {
this.kind = kind; // "pawn" etc. var newpiece = { kind, colour}
this.colour = colour; // "white"/"black"
this.sprite = colour + '_' + kind; // for draw2d.image newpiece.sprite = colour + '_' + kind; // for draw2d.image
this.captured = false; newpiece.captured = false;
this.coord = [0,0]; newpiece.coord = [0,0];
return newpiece
} }
Piece.prototype.toString = function () {
return character(this.colour) + upper(character(this.kind));
};
function startingPosition(grid) { function startingPosition(grid) {
var W = 'white', B = 'black', x; var W = 'white', B = 'black', x;
// pawns // pawns
for (x = 0; x < 8; x++) { for (x = 0; x < 8; x++) {
grid.add(new Piece('pawn', W), [x, 6]); grid.add(Piece('pawn', W), [x, 6]);
grid.add(new Piece('pawn', B), [x, 1]); grid.add(Piece('pawn', B), [x, 1]);
} }
// major pieces // major pieces
var back = ['rook','knight','bishop','queen','king','bishop','knight','rook']; var back = ['rook','knight','bishop','queen','king','bishop','knight','rook'];
for (x = 0; x < 8; x++) { for (x = 0; x < 8; x++) {
grid.add(new Piece(back[x], W), [x, 7]); grid.add(Piece(back[x], W), [x, 7]);
grid.add(new Piece(back[x], B), [x, 0]); grid.add(Piece(back[x], B), [x, 0]);
} }
} }

View File

@@ -75,7 +75,7 @@ this.update = function(dt) {
// Eat apple? // Eat apple?
if (head.x == apple.x && head.y == apple.y) spawnApple() if (head.x == apple.x && head.y == apple.y) spawnApple()
else snake.pop() else pop(snake)
} }
this.hud = function() { this.hud = function() {

View File

@@ -58,8 +58,8 @@ function initBoard() {
board = [] board = []
for (var r=0; r<ROWS; r++) { for (var r=0; r<ROWS; r++) {
var row = [] var row = []
for (var c=0; c<COLS; c++) row.push(0) for (var c=0; c<COLS; c++) push(row, 0)
board.push(row) push(board, row)
} }
} }
initBoard() initBoard()
@@ -119,10 +119,10 @@ function clearLines() {
if (every(board[r], cell => cell)) { if (every(board[r], cell => cell)) {
lines++ lines++
// remove row // remove row
board.splice(r,1) board = array(array(board, 0, r), array(board, r+1))
// add empty row on top // add empty row on top
var newRow = [] var newRow = []
for (var c=0; c<COLS; c++) newRow.push(0) for (var c=0; c<COLS; c++) push(newRow, 0)
board.unshift(newRow) board.unshift(newRow)
} else { } else {
r-- r--

View File

@@ -118,14 +118,14 @@ film2d.register = function(drawable) {
// Index by plane // Index by plane
var plane = drawable.plane || 'default' var plane = drawable.plane || 'default'
if (!plane_index[plane]) plane_index[plane] = [] if (!plane_index[plane]) plane_index[plane] = []
plane_index[plane].push(id) push(plane_index[plane], id)
// Index by groups (effect routing only) // Index by groups (effect routing only)
var groups = drawable.groups || [] var groups = drawable.groups || []
for (var i = 0; i < length(groups); i++) { for (var i = 0; i < length(groups); i++) {
var g = groups[i] var g = groups[i]
if (!group_index[g]) group_index[g] = [] if (!group_index[g]) group_index[g] = []
group_index[g].push(id) push(group_index[g], id)
} }
return id return id
@@ -140,7 +140,8 @@ film2d.unregister = function(id) {
var plane = drawable.plane || 'default' var plane = drawable.plane || 'default'
if (plane_index[plane]) { if (plane_index[plane]) {
var idx = find(plane_index[plane], id_str) var idx = find(plane_index[plane], id_str)
if (idx != null) plane_index[plane].splice(idx, 1) if (idx != null)
plane_index[plane] = array(array(plane_index[plane], 0, idx), array(plane_index[plane], idx+1))
} }
// Remove from group indices // Remove from group indices
@@ -149,7 +150,8 @@ film2d.unregister = function(id) {
var g = groups[i] var g = groups[i]
if (group_index[g]) { if (group_index[g]) {
var idx = find(group_index[g], id_str) var idx = find(group_index[g], id_str)
if (idx != null) group_index[g].splice(idx, 1) if (idx != null)
group_index[g] = array(array(group_index[g], 0, idx), array(group_index[g], idx+1))
} }
} }
@@ -159,13 +161,14 @@ film2d.unregister = function(id) {
film2d.index_group = function(id, group) { film2d.index_group = function(id, group) {
if (!group_index[group]) group_index[group] = [] if (!group_index[group]) group_index[group] = []
if (search(group_index[group], text(id)) == null) if (search(group_index[group], text(id)) == null)
group_index[group].push(text(id)) push(group_index[group], text(id))
} }
film2d.unindex_group = function(id, group) { film2d.unindex_group = function(id, group) {
if (!group_index[group]) return if (!group_index[group]) return
var idx = search(group_index[group], text(id)) var idx = search(group_index[group], text(id))
if (idx != null) group_index[group].splice(idx, 1) if (idx != null)
group_index[group] = array(array(group_index[group], 0, idx), array(group_index[group], idx+1))
} }
film2d.reindex = function(id, old_groups, new_groups) { film2d.reindex = function(id, old_groups, new_groups) {
@@ -192,9 +195,9 @@ film2d.query = function(selector) {
// If also filtering by group, check membership // If also filtering by group, check membership
if (selector.group) { if (selector.group) {
var groups = d.groups || [] var groups = d.groups || []
if (search(groups, selector.group) != null) result.push(d) if (search(groups, selector.group) != null) push(result, d)
} else { } else {
result.push(d) push(result, d)
} }
} }
} }
@@ -206,7 +209,7 @@ film2d.query = function(selector) {
var ids = group_index[selector.group] || [] var ids = group_index[selector.group] || []
for (var i = 0; i < length(ids); i++) { for (var i = 0; i < length(ids); i++) {
var d = registry[ids[i]] var d = registry[ids[i]]
if (d && d.visible != false) result.push(d) if (d && d.visible != false) push(result, d)
} }
return result return result
} }
@@ -219,7 +222,7 @@ film2d.query = function(selector) {
if (!seen[ids[i]]) { if (!seen[ids[i]]) {
seen[ids[i]] = true seen[ids[i]] = true
var d = registry[ids[i]] var d = registry[ids[i]]
if (d && d.visible != false) result.push(d) if (d && d.visible != false) push(result, d)
} }
} }
} }
@@ -242,7 +245,7 @@ film2d.get_groups = function(id) {
film2d.all_groups = function() { film2d.all_groups = function() {
var groups = [] var groups = []
arrfor(array(group_index), g => { arrfor(array(group_index), g => {
if (length(group_index[g]) > 0) groups.push(g) if (length(group_index[g]) > 0) push(groups, g)
}) })
return groups return groups
} }
@@ -282,7 +285,7 @@ film2d.render = function(params, render_backend) {
b = [] b = []
buckets[layer_key] = b buckets[layer_key] = b
} }
b.push(d) push(b, d)
} }
// Sort layers numerically (keys are text) // Sort layers numerically (keys are text)
@@ -305,32 +308,32 @@ film2d.render = function(params, render_backend) {
if (!y_down) b = reverse(b) // y_up => smaller y draws later => reverse if (!y_down) b = reverse(b) // y_up => smaller y draws later => reverse
} }
for (var j = 0; j < length(b); j++) sorted_drawables.push(b[j]) for (var j = 0; j < length(b); j++) push(sorted_drawables, b[j])
} }
drawables = sorted_drawables drawables = sorted_drawables
var commands = [] var commands = []
commands.push({ cmd: "begin_render", target: target, clear: clear_color, target_size: target_size }) push(commands, { cmd: "begin_render", target: target, clear: clear_color, target_size: target_size })
commands.push({ cmd: "set_camera", camera: camera }) push(commands, { cmd: "set_camera", camera: camera })
var batches = _batch_drawables(drawables) var batches = _batch_drawables(drawables)
for (var i = 0; i < length(batches); i++) { for (var i = 0; i < length(batches); i++) {
var batch = batches[i] var batch = batches[i]
if (batch.type == "sprite_batch") if (batch.type == "sprite_batch")
commands.push({ cmd: "draw_batch", batch_type: "sprites", geometry: { sprites: batch.sprites }, texture: batch.texture, material: batch.material }) push(commands, { cmd: "draw_batch", batch_type: "sprites", geometry: { sprites: batch.sprites }, texture: batch.texture, material: batch.material })
else if (batch.type == "mesh2d_batch") else if (batch.type == "mesh2d_batch")
commands.push({ cmd: "draw_mesh2d", meshes: batch.meshes, texture: batch.texture, material: batch.material }) push(commands, { cmd: "draw_mesh2d", meshes: batch.meshes, texture: batch.texture, material: batch.material })
else if (batch.type == "text") else if (batch.type == "text")
commands.push({ cmd: "draw_text", drawable: batch.drawable }) push(commands, { cmd: "draw_text", drawable: batch.drawable })
else if (batch.type == "texture_ref") else if (batch.type == "texture_ref")
commands.push({ cmd: "draw_texture_ref", drawable: batch.drawable }) push(commands, { cmd: "draw_texture_ref", drawable: batch.drawable })
else if (batch.type == "shape") else if (batch.type == "shape")
commands.push({ cmd: "draw_shape", drawable: batch.drawable }) push(commands, { cmd: "draw_shape", drawable: batch.drawable })
} }
commands.push({ cmd: "end_render" }) push(commands, { cmd: "end_render" })
return { commands: commands } return { commands: commands }
} }
@@ -350,9 +353,9 @@ function _batch_drawables(drawables) {
var mat = d.material || {blend: 'alpha', sampler: d.filter || 'nearest'} var mat = d.material || {blend: 'alpha', sampler: d.filter || 'nearest'}
if (current && current.type == 'sprite_batch' && current.texture == tex && _mat_eq(current.material, mat)) { if (current && current.type == 'sprite_batch' && current.texture == tex && _mat_eq(current.material, mat)) {
current.sprites.push(d) push(current.sprites, d)
} else { } else {
if (current) batches.push(current) if (current) push(batches, current)
current = {type: 'sprite_batch', texture: tex, material: mat, sprites: [d]} current = {type: 'sprite_batch', texture: tex, material: mat, sprites: [d]}
} }
} else if (d.type == 'particles') { } else if (d.type == 'particles') {
@@ -379,9 +382,9 @@ function _batch_drawables(drawables) {
} }
if (current && current.type == 'sprite_batch' && current.texture == tex && _mat_eq(current.material, mat)) { if (current && current.type == 'sprite_batch' && current.texture == tex && _mat_eq(current.material, mat)) {
current.sprites.push(sprite) push(current.sprites, sprite)
} else { } else {
if (current) batches.push(current) if (current) push(batches, current)
current = {type: 'sprite_batch', texture: tex, material: mat, sprites: [sprite]} current = {type: 'sprite_batch', texture: tex, material: mat, sprites: [sprite]}
} }
} }
@@ -422,9 +425,9 @@ function _batch_drawables(drawables) {
var tex = img var tex = img
var mat = default_mat var mat = default_mat
if (current && current.type == 'sprite_batch' && current.texture == tex && _mat_eq(current.material, mat)) { if (current && current.type == 'sprite_batch' && current.texture == tex && _mat_eq(current.material, mat)) {
current.sprites.push(sprite) push(current.sprites, sprite)
} else { } else {
if (current) batches.push(current) if (current) push(batches, current)
current = {type: 'sprite_batch', texture: tex, material: mat, sprites: [sprite]} current = {type: 'sprite_batch', texture: tex, material: mat, sprites: [sprite]}
} }
} }
@@ -435,28 +438,28 @@ function _batch_drawables(drawables) {
var mat = d.material || {blend: d.blend || 'alpha', sampler: d.filter || 'linear'} var mat = d.material || {blend: d.blend || 'alpha', sampler: d.filter || 'linear'}
if (current && current.type == 'mesh2d_batch' && current.texture == tex && _mat_eq(current.material, mat)) { if (current && current.type == 'mesh2d_batch' && current.texture == tex && _mat_eq(current.material, mat)) {
current.meshes.push(d) push(current.meshes, d)
} else { } else {
if (current) batches.push(current) if (current) push(batches, current)
current = {type: 'mesh2d_batch', texture: tex, material: mat, meshes: [d]} current = {type: 'mesh2d_batch', texture: tex, material: mat, meshes: [d]}
} }
} else if (d.type == 'shape') { } else if (d.type == 'shape') {
// Shapes are rendered individually (each has unique SDF params) // Shapes are rendered individually (each has unique SDF params)
if (current) { if (current) {
batches.push(current) push(batches, current)
current = null current = null
} }
batches.push({type: 'shape', drawable: d}) push(batches, {type: 'shape', drawable: d})
} else { } else {
if (current) { if (current) {
batches.push(current) push(batches, current)
current = null current = null
} }
batches.push({type: d.type, drawable: d}) push(batches, {type: d.type, drawable: d})
} }
} }
if (current) batches.push(current) if (current) push(batches, current)
return batches return batches
} }

View File

@@ -67,7 +67,7 @@ fx_graph.add_node = function(type, params) {
params: params, params: params,
output: {node_id: this.next_id - 1, slot: 'output'} output: {node_id: this.next_id - 1, slot: 'output'}
} }
this.nodes.push(node) push(this.nodes, node)
return node return node
} }
@@ -203,7 +203,7 @@ NODE_EXECUTORS.composite = function(params, backend) {
// Emit composite_textures command (handled outside render pass) // Emit composite_textures command (handled outside render pass)
var commands = [] var commands = []
commands.push({ push(commands, {
cmd: 'composite_textures', cmd: 'composite_textures',
base: base.target, base: base.target,
overlay: overlay.target, overlay: overlay.target,
@@ -233,7 +233,7 @@ NODE_EXECUTORS.mask = function(params, backend) {
// Emit apply_mask command (handled via shader pass outside render pass) // Emit apply_mask command (handled via shader pass outside render pass)
var commands = [] var commands = []
commands.push({ push(commands, {
cmd: 'apply_mask', cmd: 'apply_mask',
content_texture: content.target, content_texture: content.target,
mask_texture: mask.target, mask_texture: mask.target,
@@ -264,12 +264,12 @@ NODE_EXECUTORS.clip_rect = function(params, backend) {
} }
} }
commands.splice(insert_idx, 0, {cmd: 'scissor', rect: rect}) commands = array(array(array(commands, 0, insert_idx), [{cmd: 'scissor', rect: rect}]), array(commands, insert_idx))
// Add scissor reset before end_render // Add scissor reset before end_render
for (var i = length(commands) - 1; i >= 0; i--) { for (var i = length(commands) - 1; i >= 0; i--) {
if (commands[i].cmd == 'end_render') { if (commands[i].cmd == 'end_render') {
commands.splice(i, 0, {cmd: 'scissor', rect: null}) commands = array(array(array(commands, 0, i), [{cmd: 'scissor', rect:null}]) ,array(commands, i+1))
break break
} }
} }
@@ -305,7 +305,7 @@ NODE_EXECUTORS.blit = function(params, backend) {
} }
var commands = [] var commands = []
commands.push({ push(commands, {
cmd: 'blit', cmd: 'blit',
texture: src_target, texture: src_target,
target: target, target: target,
@@ -321,7 +321,7 @@ NODE_EXECUTORS.present = function(params, backend) {
var input = params.input var input = params.input
var commands = [] var commands = []
commands.push({cmd: 'present'}) push(commands, {cmd: 'present'})
return {commands: commands} return {commands: commands}
} }
@@ -350,7 +350,7 @@ NODE_EXECUTORS.shader_pass = function(params, backend) {
} }
var commands = [] var commands = []
commands.push({ push(commands, {
cmd: 'shader_pass', cmd: 'shader_pass',
shader: shader, shader: shader,
input: src, input: src,

View File

@@ -41,7 +41,7 @@ gesture.on_input = function(action_id, action) {
} else if (touchCount == 2) { } else if (touchCount == 2) {
// Two touches - potential pinch // Two touches - potential pinch
this.gestureState = 'multi' this.gestureState = 'multi'
var fingers = object.values(this.touches) var fingers = array(array(this.touches), k => this.touches[k])
this.startDist = this.dist(fingers[0], fingers[1]) this.startDist = this.dist(fingers[0], fingers[1])
} }
} }
@@ -55,7 +55,7 @@ gesture.on_input = function(action_id, action) {
if (touchCount == 2 && this.gestureState == 'multi') { if (touchCount == 2 && this.gestureState == 'multi') {
// Check for pinch gesture // Check for pinch gesture
var fingers = object.values(this.touches) var fingers = array(array(this.touches), k => this.touches[k])
var currentDist = this.dist(fingers[0], fingers[1]) var currentDist = this.dist(fingers[0], fingers[1])
var d = currentDist - this.startDist var d = currentDist - this.startDist

View File

@@ -154,7 +154,7 @@ function create_image(path){
try{ try{
def bytes = io.slurp(path); def bytes = io.slurp(path);
var ext = array(path, '.').pop() var ext = pop(array(path, '.'))
var raw = decode_image(bytes, ext); var raw = decode_image(bytes, ext);
/* ── Case A: single surface (from make_texture) ────────────── */ /* ── Case A: single surface (from make_texture) ────────────── */
@@ -357,7 +357,7 @@ graphics.texture = function texture(path) {
} }
graphics.tex_hotreload = function tex_hotreload(file) { graphics.tex_hotreload = function tex_hotreload(file) {
var basename = array(array(file, '/').pop(), '.')[0] var basename = array(pop(array(file, '/')), '.')[0]
// Check if this basename exists in our cache // Check if this basename exists in our cache
if (!(basename in cache)) return if (!(basename in cache)) return

View File

@@ -93,13 +93,13 @@ function create_user(index, config) {
// Push entity onto control stack // Push entity onto control stack
push: function(entity) { push: function(entity) {
this.control_stack.push(entity) push(this.control_stack, entity)
}, },
// Pop from control stack // Pop from control stack
pop: function() { pop: function() {
if (length(this.control_stack) > 1) { if (length(this.control_stack) > 1) {
return this.control_stack.pop() return pop(this.control_stack)
} }
return null return null
}, },
@@ -166,7 +166,7 @@ function pick_user(canon) {
user.active_device = canon.device_id user.active_device = canon.device_id
if (find(user.paired_devices, canon.device_id) == null) { if (find(user.paired_devices, canon.device_id) == null) {
user.paired_devices.push(canon.device_id) push(user.paired_devices, canon.device_id)
} }
} }
} }
@@ -207,7 +207,7 @@ function configure(opts) {
// Create users // Create users
_users = [] _users = []
for (var i = 0; i < _config.max_users; i++) { for (var i = 0; i < _config.max_users; i++) {
_users.push(create_user(i, _config)) push(_users, create_user(i, _config))
} }
_initialized = true _initialized = true

View File

@@ -135,7 +135,8 @@ function make(defaults, display_names) {
// Remove from other actions // Remove from other actions
arrfor(array(this.action_map), act => { arrfor(array(this.action_map), act => {
var idx = search(this.action_map[act], new_control) var idx = search(this.action_map[act], new_control)
if (idx >= 0) this.action_map[act].splice(idx, 1) if (idx >= 0)
this.action_map[act] = array(array(this.action_map[act], 0, idx), array(this.action_map[act], idx+1))
}) })
// Clear existing bindings for this device kind // Clear existing bindings for this device kind
@@ -143,9 +144,8 @@ function make(defaults, display_names) {
for (var i = length(target) - 1; i >= 0; i--) { for (var i = length(target) - 1; i >= 0; i--) {
var existing_kind = starts_with(target[i], 'gamepad_') ? 'gamepad' : var existing_kind = starts_with(target[i], 'gamepad_') ? 'gamepad' :
starts_with(target[i], 'swipe_') ? 'touch' : 'keyboard' starts_with(target[i], 'swipe_') ? 'touch' : 'keyboard'
if (existing_kind == device_kind) { if (existing_kind == device_kind)
target.splice(i, 1) this.action_map[action] = array(array(this.action_map[action], 0, i), array(this.action_map[action], i+1))
}
} }
// Add new binding // Add new binding

View File

@@ -40,7 +40,7 @@ function gesture_stage(config) {
// Only process gamepad touchpad events // Only process gamepad touchpad events
if (ev.control != 'gamepad_touchpad') { if (ev.control != 'gamepad_touchpad') {
output.push(ev) push(output, ev)
continue continue
} }
@@ -72,7 +72,7 @@ function gesture_stage(config) {
var d = currentDist - start_dist var d = currentDist - start_dist
if (Math.abs(d) >= pinch_th / 100) { if (Math.abs(d) >= pinch_th / 100) {
output.push({ push(output, {
kind: 'gesture', kind: 'gesture',
device_id: ev.device_id, device_id: ev.device_id,
control: d > 0 ? 'pinch_out' : 'pinch_in', control: d > 0 ? 'pinch_out' : 'pinch_in',
@@ -103,7 +103,7 @@ function gesture_stage(config) {
? (dx > 0 ? 'swipe_right' : 'swipe_left') ? (dx > 0 ? 'swipe_right' : 'swipe_left')
: (dy > 0 ? 'swipe_down' : 'swipe_up') : (dy > 0 ? 'swipe_down' : 'swipe_up')
output.push({ push(output, {
kind: 'gesture', kind: 'gesture',
device_id: ev.device_id, device_id: ev.device_id,
control: dir, control: dir,
@@ -139,18 +139,18 @@ function emacs_stage() {
// Only process keyboard button events // Only process keyboard button events
if (ev.device_id != 'kbm' || ev.kind != 'button' || !ev.pressed) { if (ev.device_id != 'kbm' || ev.kind != 'button' || !ev.pressed) {
output.push(ev) push(output, ev)
continue continue
} }
if (find(valid_emacs_keys, ev.control) == null) { if (find(valid_emacs_keys, ev.control) == null) {
output.push(ev) push(output, ev)
continue continue
} }
// Only process if we have modifiers OR waiting for chord // Only process if we have modifiers OR waiting for chord
if (!ev.mods?.ctrl && !ev.mods?.alt && !prefix) { if (!ev.mods?.ctrl && !ev.mods?.alt && !prefix) {
output.push(ev) push(output, ev)
continue continue
} }
@@ -174,7 +174,7 @@ function emacs_stage() {
if (prefix) { if (prefix) {
var chord = prefix + " " + notation var chord = prefix + " " + notation
prefix = null prefix = null
output.push({ push(output, {
kind: 'chord', kind: 'chord',
device_id: ev.device_id, device_id: ev.device_id,
control: chord, control: chord,
@@ -183,7 +183,7 @@ function emacs_stage() {
time: ev.time time: ev.time
}) })
} else { } else {
output.push({ push(output, {
kind: 'chord', kind: 'chord',
device_id: ev.device_id, device_id: ev.device_id,
control: notation, control: notation,
@@ -213,14 +213,14 @@ function action_stage(bindings) {
// Pass through non-button events // Pass through non-button events
if (ev.kind != 'button' && ev.kind != 'chord' && ev.kind != 'gesture') { if (ev.kind != 'button' && ev.kind != 'chord' && ev.kind != 'gesture') {
output.push(ev) push(output, ev)
continue continue
} }
var actions = bindings.get_actions(ev.control) var actions = bindings.get_actions(ev.control)
if (length(actions) == 0) { if (length(actions) == 0) {
output.push(ev) push(output, ev)
continue continue
} }
@@ -230,7 +230,7 @@ function action_stage(bindings) {
if (ev.pressed) down[action] = true if (ev.pressed) down[action] = true
else if (ev.released) down[action] = false else if (ev.released) down[action] = false
output.push({ push(output, {
kind: 'action', kind: 'action',
device_id: ev.device_id, device_id: ev.device_id,
control: action, control: action,
@@ -277,16 +277,16 @@ function make(user, config) {
var action = null var action = null
if (config.gestures != false) { if (config.gestures != false) {
stages.push(gesture_stage(config)) push(stages, gesture_stage(config))
} }
if (config.emacs != false) { if (config.emacs != false) {
stages.push(emacs_stage()) push(stages, emacs_stage())
} }
action = action_stage(user.bindings) action = action_stage(user.bindings)
stages.push(action) push(stages, action)
stages.push(delivery_stage(user)) push(stages, delivery_stage(user))
return { return {
stages: stages, stages: stages,

View File

@@ -81,9 +81,9 @@ function build_polyline_mesh(line) {
for (var i = 0; i < length(points); i++) { for (var i = 0; i < length(points); i++) {
var p = points[i] var p = points[i]
if (points_space == 'local') { if (points_space == 'local') {
pts.push({x: p.x + pos.x, y: p.y + pos.y}) push(pts, {x: p.x + pos.x, y: p.y + pos.y})
} else { } else {
pts.push({x: p.x, y: p.y}) push(pts, {x: p.x, y: p.y})
} }
} }
@@ -92,7 +92,7 @@ function build_polyline_mesh(line) {
for (var i = 1; i < length(pts); i++) { for (var i = 1; i < length(pts); i++) {
var dx = pts[i].x - pts[i-1].x var dx = pts[i].x - pts[i-1].x
var dy = pts[i].y - pts[i-1].y var dy = pts[i].y - pts[i-1].y
cumulative.push(cumulative[i-1] + math.sqrt(dx*dx + dy*dy)) push(cumulative, cumulative[i-1] + math.sqrt(dx*dx + dy*dy))
} }
var total_length = cumulative[length(cumulative) - 1] var total_length = cumulative[length(cumulative) - 1]
@@ -179,7 +179,7 @@ function build_polyline_mesh(line) {
n.y = dx n.y = dx
} }
normals.push(n) push(normals, n)
} }
// Generate vertices (2 per point - left and right of line) // Generate vertices (2 per point - left and right of line)
@@ -190,7 +190,7 @@ function build_polyline_mesh(line) {
var u = get_u(i) var u = get_u(i)
// Left vertex (v=0) // Left vertex (v=0)
verts.push({ push(verts, {
x: p.x + n.x * w, x: p.x + n.x * w,
y: p.y + n.y * w, y: p.y + n.y * w,
u: u, u: u,
@@ -199,7 +199,7 @@ function build_polyline_mesh(line) {
}) })
// Right vertex (v=1) // Right vertex (v=1)
verts.push({ push(verts, {
x: p.x - n.x * w, x: p.x - n.x * w,
y: p.y - n.y * w, y: p.y - n.y * w,
u: u, u: u,
@@ -212,24 +212,24 @@ function build_polyline_mesh(line) {
for (var i = 0; i < length(pts) - 1; i++) { for (var i = 0; i < length(pts) - 1; i++) {
var base = i * 2 var base = i * 2
// First triangle // First triangle
indices.push(base + 0) push(indices, base + 0)
indices.push(base + 1) push(indices, base + 1)
indices.push(base + 2) push(indices, base + 2)
// Second triangle // Second triangle
indices.push(base + 1) push(indices, base + 1)
indices.push(base + 3) push(indices, base + 3)
indices.push(base + 2) push(indices, base + 2)
} }
// Handle closed path // Handle closed path
if (closed && length(pts) > 2) { if (closed && length(pts) > 2) {
var last = (length(pts) - 1) * 2 var last = (length(pts) - 1) * 2
indices.push(last + 0) push(indices, last + 0)
indices.push(last + 1) push(indices, last + 1)
indices.push(0) push(indices, 0)
indices.push(last + 1) push(indices, last + 1)
indices.push(1) push(indices, 1)
indices.push(0) push(indices, 0)
} }
// Add round caps if requested // Add round caps if requested
@@ -259,7 +259,7 @@ function add_round_cap(verts, indices, p, n, width, u, v_offset, v_scale, is_sta
var dy = is_start ? n.x : -n.x var dy = is_start ? n.x : -n.x
// Center vertex // Center vertex
verts.push({ push(verts, {
x: p.x, x: p.x,
y: p.y, y: p.y,
u: u, u: u,
@@ -274,7 +274,7 @@ function add_round_cap(verts, indices, p, n, width, u, v_offset, v_scale, is_sta
var cx = math.cosine(angle) var cx = math.cosine(angle)
var cy = math.sine(angle) var cy = math.sine(angle)
verts.push({ push(verts, {
x: p.x + cx * w, x: p.x + cx * w,
y: p.y + cy * w, y: p.y + cy * w,
u: u, u: u,
@@ -285,9 +285,9 @@ function add_round_cap(verts, indices, p, n, width, u, v_offset, v_scale, is_sta
// Fan triangles // Fan triangles
for (var i = 0; i < segments; i++) { for (var i = 0; i < segments; i++) {
indices.push(base_idx) push(indices, base_idx)
indices.push(base_idx + 1 + i) push(indices, base_idx + 1 + i)
indices.push(base_idx + 2 + i) push(indices, base_idx + 2 + i)
} }
} }
@@ -307,17 +307,17 @@ function add_square_cap(verts, indices, p, n, width, u, v_offset, v_scale, is_st
var ey = p.y + dy * ext var ey = p.y + dy * ext
// Four corners of the cap // Four corners of the cap
verts.push({x: p.x + n.x * w, y: p.y + n.y * w, u: u, v: v_offset, r: 1, g: 1, b: 1, a: 1}) push(verts, {x: p.x + n.x * w, y: p.y + n.y * w, u: u, v: v_offset, r: 1, g: 1, b: 1, a: 1})
verts.push({x: p.x - n.x * w, y: p.y - n.y * w, u: u, v: v_scale + v_offset, r: 1, g: 1, b: 1, a: 1}) push(verts, {x: p.x - n.x * w, y: p.y - n.y * w, u: u, v: v_scale + v_offset, r: 1, g: 1, b: 1, a: 1})
verts.push({x: ex + n.x * w, y: ey + n.y * w, u: u, v: v_offset, r: 1, g: 1, b: 1, a: 1}) push(verts, {x: ex + n.x * w, y: ey + n.y * w, u: u, v: v_offset, r: 1, g: 1, b: 1, a: 1})
verts.push({x: ex - n.x * w, y: ey - n.y * w, u: u, v: v_scale + v_offset, r: 1, g: 1, b: 1, a: 1}) push(verts, {x: ex - n.x * w, y: ey - n.y * w, u: u, v: v_scale + v_offset, r: 1, g: 1, b: 1, a: 1})
indices.push(base_idx + 0) push(indices, base_idx + 0)
indices.push(base_idx + 1) push(indices, base_idx + 1)
indices.push(base_idx + 2) push(indices, base_idx + 2)
indices.push(base_idx + 1) push(indices, base_idx + 1)
indices.push(base_idx + 3) push(indices, base_idx + 3)
indices.push(base_idx + 2) push(indices, base_idx + 2)
} }
var defaults = { var defaults = {

View File

@@ -18,7 +18,7 @@ var particles2d_proto = {
var emitters = { var emitters = {
// Spawn a particle for an emitter // Spawn a particle for an emitter
spawn: function(emitter) { spawn: function(emitter) {
emitter.particles.push({ push(emitter.particles, {
pos: { pos: {
x: emitter.pos.x + (random() - 0.5) * emitter.spawn_area.width, x: emitter.pos.x + (random() - 0.5) * emitter.spawn_area.width,
y: emitter.pos.y + (random() - 0.5) * emitter.spawn_area.height y: emitter.pos.y + (random() - 0.5) * emitter.spawn_area.height
@@ -76,17 +76,13 @@ var emitters = {
alpha = 1 - (p.time - p.life * 0.7) / (p.life * 0.3) alpha = 1 - (p.time - p.life * 0.7) / (p.life * 0.3)
} }
p.color.a = alpha p.color.a = alpha
// Remove dead particles
if (p.time >= p.life) {
emitter.particles.splice(i, 1)
}
} }
emitter.particles = filter(emitter.particles, p => p.time < p.life)
// Sync to film2d if handle provided // Sync to film2d if handle provided
if (emitter.handle) { if (emitter.handle)
emitter.handle.particles = emitter.particles emitter.handle.particles = emitter.particles
}
}, },
// Create an emitter config // Create an emitter config

View File

@@ -56,7 +56,7 @@ PlaydateBackend.prototype.get_or_create_target = function(width, height, key) {
key: key key: key
} }
this.target_pool[pool_key].push(target) push(this.target_pool[pool_key], target)
return target return target
} }

View File

@@ -91,7 +91,7 @@ rasterize.ellipse = function ellipse(pos, radii, opt) {
within_wedge(dx+1, dy, start, end, full_circle) within_wedge(dx+1, dy, start, end, full_circle)
if (last || !next_in_ring) { if (last || !next_in_ring) {
strips.push({ push(strips, {
x: run_start, x: run_start,
y: y_screen, y: y_screen,
width: (cx + dx) - run_start + 1, width: (cx + dx) - run_start + 1,
@@ -176,7 +176,7 @@ rasterize.round_rect = function round_rect(rect, radius, thickness) {
var w = dx_out - dx_in var w = dx_out - dx_in
if (w <= 0) continue if (w <= 0) continue
strips.push( push(strips,
{ x:cx_l - dx_out, y:cy_t - dy, width:w, height:1 }, { x:cx_l - dx_out, y:cy_t - dy, width:w, height:1 },
{ x:cx_r + dx_in + 1, y:cy_t - dy, width:w, height:1 }, { x:cx_r + dx_in + 1, y:cy_t - dy, width:w, height:1 },
{ x:cx_l - dx_out, y:cy_b + dy, width:w, height:1 }, { x:cx_l - dx_out, y:cy_b + dy, width:w, height:1 },
@@ -209,7 +209,7 @@ rasterize.fill_round_rect = function fill_round_rect(rect, radius) {
var dx = floor(math.sqrt(radius * radius - dy * dy)) var dx = floor(math.sqrt(radius * radius - dy * dy))
var w = (dx << 1) + 1 var w = (dx << 1) + 1
caps.push( push(caps,
{ x:cx_l - dx, y:cy_t - dy, width:w, height:1 }, { x:cx_l - dx, y:cy_t - dy, width:w, height:1 },
{ x:cx_r - dx, y:cy_t - dy, width:w, height:1 }, { x:cx_r - dx, y:cy_t - dy, width:w, height:1 },
{ x:cx_l - dx, y:cy_b + dy, width:w, height:1 }, { x:cx_l - dx, y:cy_b + dy, width:w, height:1 },

View File

@@ -100,7 +100,7 @@ function read_ignore(dir) {
arrfor(lines, function(line) { arrfor(lines, function(line) {
line = trim(line) line = trim(line)
if (!line || starts_with(line, '#')) return if (!line || starts_with(line, '#')) return
patterns.push(line) push(patterns, line)
}) })
} }
return patterns return patterns
@@ -120,7 +120,7 @@ Resources.getAllFiles = function(dir = "") {
if (!st.filesize) return if (!st.filesize) return
var ext = getExtension(f) var ext = getExtension(f)
if (!isRecognizedExtension(ext)) return if (!isRecognizedExtension(ext)) return
results.push(fullPath) push(results, fullPath)
} catch(e) {} } catch(e) {}
}) })
return results return results

View File

@@ -700,7 +700,7 @@ function _load_image_file(path) {
var decoded var decoded
if (!bytes) return null if (!bytes) return null
var ext = lower(array(path, '.').pop()) var ext = lower(pop(array(path, '.')))
var surface = null var surface = null
switch (ext) { switch (ext) {
@@ -822,7 +822,7 @@ sdl_gpu.get_or_create_target = function(width, height, key) {
key: key key: key
} }
_target_pool[pool_key].push(target) push(_target_pool[pool_key], target)
return target return target
} }
@@ -1202,23 +1202,23 @@ function _execute_commands(commands, window_size) {
break break
case 'draw_batch': case 'draw_batch':
pending_draws.push(cmd) push(pending_draws, cmd)
break break
case 'draw_text': case 'draw_text':
pending_draws.push(cmd) push(pending_draws, cmd)
break break
case 'draw_texture_ref': case 'draw_texture_ref':
pending_draws.push(cmd) push(pending_draws, cmd)
break break
case 'draw_shape': case 'draw_shape':
pending_draws.push(cmd) push(pending_draws, cmd)
break break
case 'draw_mesh2d': case 'draw_mesh2d':
pending_draws.push(cmd) push(pending_draws, cmd)
break break
case 'blit': case 'blit':
@@ -2219,7 +2219,7 @@ function _do_shader_pass(cmd_buffer, cmd, get_swapchain_tex) {
var samplers = [{texture: input.texture, sampler: _sampler_linear}] var samplers = [{texture: input.texture, sampler: _sampler_linear}]
if (cmd.extra_inputs) { if (cmd.extra_inputs) {
arrfor(cmd.extra_inputs, function(extra) { arrfor(cmd.extra_inputs, function(extra) {
samplers.push({texture: extra.texture, sampler: _sampler_linear}) push(samplers, {texture: extra.texture, sampler: _sampler_linear})
}) })
} }
pass.bind_fragment_samplers(0, samplers) pass.bind_fragment_samplers(0, samplers)

View File

@@ -87,16 +87,6 @@ function loop()
render.present() render.present()
dt = os.now() - now dt = os.now() - now
fps_samples.push(dt)
if (length(fps_samples) > fps_window) fps_samples.shift()
if (now - last_fps_update >= fps_update_period) {
var sum = 0
arrfor(fps_samples, x => sum += x)
prosperon.window.title = `Bunnymark [fps: ${(length(fps_samples)/sum).toFixed(1)}]`;
last_fps_update = now
}
var delay = (1/60) - dt var delay = (1/60) - dt
$delay(loop, delay) $delay(loop, delay)
} }

View File

@@ -23,7 +23,7 @@ for (var i = 0; i < length(formats); i++) {
if (!colorspaces[fmt.colorspace]) { if (!colorspaces[fmt.colorspace]) {
colorspaces[fmt.colorspace] = []; colorspaces[fmt.colorspace] = [];
} }
colorspaces[fmt.colorspace].push(fmt); push(colorspaces[fmt.colorspace], fmt);
} }
log.console("\nFound colorspaces:"); log.console("\nFound colorspaces:");

View File

@@ -2,7 +2,7 @@
var Surface = use('sdl3/surface'); var Surface = use('sdl3/surface');
// Test creating a surface // Test creating a surface
var surf = new Surface({width: 100, height: 100}); var surf = Surface({width: 100, height: 100});
log.console("Created surface:", surf.width, "x", surf.height); log.console("Created surface:", surf.width, "x", surf.height);
log.console(surf) log.console(surf)
@@ -26,12 +26,12 @@ var pixels = surf.pixels();
log.console("Got pixels array buffer, length:", pixels.byteLength); log.console("Got pixels array buffer, length:", pixels.byteLength);
// Test creating surface with custom format // Test creating surface with custom format
var surf4 = new Surface({width: 64, height: 64, format: "rgb24"}); var surf4 = Surface({width: 64, height: 64, format: "rgb24"});
log.console("Created RGB24 surface:", surf4.width, "x", surf4.height, "format:", surf4.format); log.console("Created RGB24 surface:", surf4.width, "x", surf4.height, "format:", surf4.format);
// Test creating surface from pixels // Test creating surface from pixels
var pixelData = new ArrayBuffer(32 * 32 * 4); // 32x32 RGBA var pixelData = ArrayBuffer(32 * 32 * 4); // 32x32 RGBA
var surf5 = new Surface({width: 32, height: 32, pixels: pixelData}); var surf5 = Surface({width: 32, height: 32, pixels: pixelData});
log.console("Created surface from pixels:", surf5.width, "x", surf5.height); log.console("Created surface from pixels:", surf5.width, "x", surf5.height);
log.console("Surface module test passed!"); log.console("Surface module test passed!");

View File

@@ -19,10 +19,10 @@ var tilemap = {
var y = pos.y - this.offset_y var y = pos.y - this.offset_y
while (length(this.tiles) <= x) while (length(this.tiles) <= x)
this.tiles.push([]) push(this.tiles, [])
while (length(this.tiles[x]) <= y) while (length(this.tiles[x]) <= y)
this.tiles[x].push(null) push(this.tiles[x], null)
this.tiles[x][y] = image this.tiles[x][y] = image
}, },

View File

@@ -8,7 +8,7 @@ function make_engine(default_clock) {
tweens: [], tweens: [],
default_clock: default_clock || null, default_clock: default_clock || null,
add(tween) { add(tween) {
this.tweens.push(tween) push(this.tweens, tween)
}, },
remove(tween) { remove(tween) {
this.tweens = filter(this.tweens, t => t != tween) this.tweens = filter(this.tweens, t => t != tween)
@@ -160,7 +160,7 @@ var TimelineProto = {
engine: null, engine: null,
add_event: function(time_value, fn) { add_event: function(time_value, fn) {
this.events.push({ time: time_value, fn, fired: false }) push(this.events, { time: time_value, fn, fired: false })
}, },
add_tween: function(obj, props, duration, start_time) { add_tween: function(obj, props, duration, start_time) {