diff --git a/action.cm b/action.cm index 5736a03e..1b2f4eb8 100644 --- a/action.cm +++ b/action.cm @@ -226,7 +226,7 @@ action.on_input = function(action_id, evt) var matched_actions = [] arrfor(array(this.action_map), mapped_action => { if (find(this.action_map[mapped_action], action_id) != null) { - matched_actions.push(mapped_action) + push(matched_actions, mapped_action) if (evt.pressed) this.down[mapped_action] = true @@ -256,14 +256,14 @@ action.rebind_action = function(action_name, new_key) { arrfor(array(this.action_map), act => { var idx = find(this.action_map[act], new_key) 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 var target_bindings = this.action_map[action_name] for (var i = length(target_bindings) - 1; i >= 0; i--) { 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 diff --git a/clay.cm b/clay.cm index 01cb039e..28b1f4c0 100644 --- a/clay.cm +++ b/clay.cm @@ -130,7 +130,7 @@ function build_drawables(node, root_height, parent_abs_x, parent_abs_y, parent_s // Background if (node.config.background_image) { if (node.config.slice) { - drawables.push({ + push(drawables, { type: 'sprite', image: node.config.background_image, 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 }) } else { - drawables.push({ + push(drawables, { type: 'sprite', image: node.config.background_image, 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) { - drawables.push({ + push(drawables, { type: 'rect', pos: {x: vis_x, y: vis_y}, width: rect.width, @@ -167,7 +167,7 @@ function build_drawables(node, root_height, parent_abs_x, parent_abs_y, parent_s // Content (Image/Text) if (node.config.image) { - drawables.push({ + push(drawables, { type: 'sprite', image: node.config.image, 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) { - drawables.push({ + push(drawables, { type: 'text', text: node.config.text, font: node.config.font_path, @@ -249,15 +249,15 @@ function push_node(configs, contain_mode) { // Add to parent var parent = tree_stack[length(tree_stack)-1] - parent.children.push(node) + push(parent.children, node) lay_ctx.insert(parent.id, item) - tree_stack.push(node) + push(tree_stack, node) return node } function pop_node() { - tree_stack.pop() + pop(tree_stack) } // Generic container diff --git a/clay_input.cm b/clay_input.cm index 67d9d4d4..bc6a42ad 100644 --- a/clay_input.cm +++ b/clay_input.cm @@ -74,7 +74,7 @@ clay_input.click = function click(tree_root, mousepos, button = 'left') { clay_input.get_actionable = function get_actionable(tree_root) { var actionable = [] function walk(node) { - if (node.config.action) actionable.push(node) + if (node.config.action) push(actionable, node) if (node[clay.CHILDREN]) 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) { var results = [] function rec(node) { - if (predicate(node)) results.push(node) + if (predicate(node)) push(results, node) if (node[clay.CHILDREN]) arrfor(node[clay.CHILDREN], rec) } diff --git a/compositor.cm b/compositor.cm index e0a67a7b..a9d34509 100644 --- a/compositor.cm +++ b/compositor.cm @@ -23,7 +23,7 @@ compositor.compile = function(config) { // Clear screen 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) var planes = config.planes || config.layers || [] @@ -41,7 +41,7 @@ compositor.compile = function(config) { } function compile_imgui_layer(layer, ctx) { - ctx.passes.push({ + push(ctx.passes, { type: 'imgui', target: 'screen', draw: layer.draw @@ -70,7 +70,7 @@ function compile_plane(plane_config, ctx, group_effects) { // Add manual drawables if (plane_config.drawables) { 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 @@ -98,14 +98,14 @@ function compile_plane(plane_config, ctx, group_effects) { if (group_effects[gname]) { if (!effect_groups[gname]) effect_groups[gname] = {sprites: [], effects: group_effects[gname].effects} - effect_groups[gname].sprites.push(s) + push(effect_groups[gname].sprites, s) assigned = true break // Only assign to first matching effect group } } // 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 @@ -113,7 +113,7 @@ function compile_plane(plane_config, ctx, group_effects) { // Clear plane 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 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') // Render group content - ctx.passes.push({ + push(ctx.passes, { type: 'render', renderer: 'film2d', drawables: eg.sprites, @@ -142,7 +142,7 @@ function compile_plane(plane_config, ctx, group_effects) { } // Composite result to plane - ctx.passes.push({ + push(ctx.passes, { type: 'composite', source: current, dest: plane_target, @@ -154,7 +154,7 @@ function compile_plane(plane_config, ctx, group_effects) { // Render base sprites (no effects) if (length(base_sprites) > 0) { - ctx.passes.push({ + push(ctx.passes, { type: 'render', renderer: 'film2d', drawables: base_sprites, @@ -167,7 +167,7 @@ function compile_plane(plane_config, ctx, group_effects) { } // Composite plane to screen - ctx.passes.push({ + push(ctx.passes, { type: 'blit_to_screen', source: plane_target, 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') // Threshold - ctx.passes.push({ + push(ctx.passes, { type: 'shader_pass', shader: 'threshold', 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_in = bright 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}}}) - 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: 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: blur1, output: blur2, uniforms: {direction: {x: 0, y: 1}, texel_size: {x: 1/size.width, y: 1/size.height}}}) blur_in = blur2 } // 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') { 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') // Render mask - ctx.passes.push({ + push(ctx.passes, { type: 'render', renderer: 'film2d', drawables: mask_sprites, @@ -225,7 +225,7 @@ function apply_effect(ctx, effect, input, size, camera, hint, current_plane, gro }) // Apply mask - ctx.passes.push({ + push(ctx.passes, { type: 'apply_mask', content: input, mask: mask_target, @@ -235,11 +235,11 @@ function apply_effect(ctx, effect, input, size, camera, hint, current_plane, gro }) } else { // No mask sprites, pass through - ctx.passes.push({type: 'blit', source: input, dest: output}) + push(ctx.passes, {type: 'blit', source: input, dest: output}) } } else { // Unknown effect, pass through - ctx.passes.push({type: 'blit', source: input, dest: output}) + push(ctx.passes, {type: 'blit', source: input, dest: output}) } return output @@ -267,8 +267,8 @@ compositor.execute = function(plan) { if (pass.type == 'clear') { var target = resolve(pass.target) - commands.push({cmd: 'begin_render', target: target, clear: pass.color}) - commands.push({cmd: 'end_render'}) + push(commands, {cmd: 'begin_render', target: target, clear: pass.color}) + push(commands, {cmd: 'end_render'}) } else if (pass.type == 'render') { var result = film2d.render({ @@ -280,10 +280,10 @@ compositor.execute = function(plan) { clear: pass.clear }, backend) 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') { - commands.push({ + push(commands, { cmd: 'shader_pass', shader: pass.shader, input: resolve(pass.input), @@ -292,7 +292,7 @@ compositor.execute = function(plan) { }) } else if (pass.type == 'composite_textures') { - commands.push({ + push(commands, { cmd: 'composite_textures', base: resolve(pass.base), overlay: resolve(pass.overlay), @@ -301,7 +301,7 @@ compositor.execute = function(plan) { }) } else if (pass.type == 'apply_mask') { - commands.push({ + push(commands, { cmd: 'apply_mask', content_texture: resolve(pass.content), mask_texture: resolve(pass.mask), @@ -311,7 +311,7 @@ compositor.execute = function(plan) { }) } else if (pass.type == 'composite') { - commands.push({ + push(commands, { cmd: 'blit', texture: resolve(pass.source), target: resolve(pass.dest), @@ -320,7 +320,7 @@ compositor.execute = function(plan) { } else if (pass.type == 'blit_to_screen') { var rect = _calc_presentation(pass.source_size, pass.dest_size, pass.presentation) - commands.push({ + push(commands, { cmd: 'blit', texture: resolve(pass.source), target: 'screen', @@ -330,14 +330,14 @@ compositor.execute = function(plan) { } else if (pass.type == 'blit') { var src = resolve(pass.source) var dst = resolve(pass.dest) - commands.push({ + push(commands, { cmd: 'blit', texture: src, target: dst, dst_rect: {x: 0, y: 0, width: dst.width, height: dst.height} }) } else if (pass.type == 'imgui') { - commands.push({ + push(commands, { cmd: 'imgui', target: resolve(pass.target), draw: pass.draw diff --git a/core.cm b/core.cm index 5c99ea47..fd4bdbce 100644 --- a/core.cm +++ b/core.cm @@ -78,7 +78,29 @@ core.backend = function() { } // FPS tracking 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 _frame_time_ms = 0 @@ -162,7 +184,7 @@ function _main_loop() { // Handle both compositor result ({commands: [...]}) and fx_graph (graph object) if (render_result.commands) { if (_config.imgui || _config.editor) { - render_result.commands.push({ + push(render_result.commands, { cmd: 'imgui', draw: function(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 actual_frame_time = frame_end - frame_start - - // Track FPS based on actual work time + _frame_time_ms = actual_frame_time * 1000 - _fps_samples.push(actual_frame_time) - if (length(_fps_samples) > _fps_sample_count) { - _fps_samples.shift() - } - var avg_frame_time = reduce(_fps_samples, function(a,b) { return a+b }) / length(_fps_samples) + fps_add_sample(actual_frame_time) + + var avg_frame_time = fps_get_avg() _current_fps = avg_frame_time > 0 ? 1 / avg_frame_time : 0 - + // Schedule next frame var frame_time = 1 / _framerate var elapsed = frame_end - frame_start diff --git a/debug_imgui.cm b/debug_imgui.cm index 0cc75366..ad4d4832 100644 --- a/debug_imgui.cm +++ b/debug_imgui.cm @@ -143,29 +143,29 @@ function _render_node_summary(imgui, node) { var info = [] 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) { - info.push("size:" + text(node.width) + "x" + text(node.height)) + push(info, "size:" + text(node.width) + "x" + text(node.height)) } if (node.image) { - info.push("img:" + node.image) + push(info, "img:" + node.image) } if (node.text) { var t = node.text if (length(t) > 20) t = text(t, 0, 17) + "..." - info.push("\"" + t + "\"") + push(info, "\"" + t + "\"") } if (node.effects && length(node.effects) > 0) { var fx = [] 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) { diff --git a/effects.cm b/effects.cm index 38fd2632..9e445881 100644 --- a/effects.cm +++ b/effects.cm @@ -36,7 +36,7 @@ effects.register('bloom', { // Threshold extraction var thresh_target = ctx.alloc_target(size.width, size.height, 'bloom_thresh') - passes.push({ + push(passes, { type: 'shader', shader: 'threshold', input: input, @@ -55,14 +55,14 @@ effects.register('bloom', { var blur_count = params.blur_passes != null ? params.blur_passes : 3 for (var i = 0; i < blur_count; i++) { - passes.push({ + push(passes, { type: 'shader', shader: 'blur', input: blur_src, output: blur_a, uniforms: {direction: {x: 2, y: 0}, texel_size: texel} }) - passes.push({ + push(passes, { type: 'shader', shader: 'blur', input: blur_a, @@ -73,7 +73,7 @@ effects.register('bloom', { } // Additive composite - passes.push({ + push(passes, { type: 'composite', base: input, overlay: blur_src, @@ -124,7 +124,7 @@ effects.register('mask', { // Render mask source to target var mask_target = ctx.alloc_target(size.width, size.height, 'mask_src') - passes.push({ + push(passes, { type: 'render_subtree', root: mask_source, output: mask_target, @@ -133,7 +133,7 @@ effects.register('mask', { }) // Apply mask shader - passes.push({ + push(passes, { type: 'shader', shader: 'mask', inputs: [input, mask_target], @@ -190,14 +190,14 @@ effects.register('blur', { var blur_count = params.passes != null ? params.passes : 2 for (var i = 0; i < blur_count; i++) { - passes.push({ + push(passes, { type: 'shader', shader: 'blur', input: src, output: blur_a, uniforms: {direction: {x: 2, y: 0}, texel_size: texel} }) - passes.push({ + push(passes, { type: 'shader', shader: 'blur', input: blur_a, @@ -208,7 +208,7 @@ effects.register('blur', { } // Final blit to output - passes.push({type: 'blit', source: src, dest: output}) + push(passes, {type: 'blit', source: src, dest: output}) return passes } }) diff --git a/examples/bunnymark/main.ce b/examples/bunnymark/main.ce index e2cac960..bb970948 100644 --- a/examples/bunnymark/main.ce +++ b/examples/bunnymark/main.ce @@ -14,7 +14,7 @@ var bunnies = [] // Start with some initial bunnies: for (var i = 0; i < 100; i++) { - bunnies.push({ + push(bunnies, { x: random.random() * config.width, y: random.random() * config.height, 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) { - // 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: var mouse = input.mousestate() if (mouse.left) for (var i = 0; i < 50; i++) { - bunnies.push({ + push(bunnies, { x: mouse.x, y: mouse.y, vx: (random.random() * 300) - 150, @@ -63,7 +53,4 @@ this.update = function(dt) { this.hud = function() { 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) } diff --git a/examples/chess/grid.cm b/examples/chess/grid.cm index d7c948c2..bd72fa48 100644 --- a/examples/chess/grid.cm +++ b/examples/chess/grid.cm @@ -26,15 +26,13 @@ var grid_prototype = { // add an entity into a cell add(entity, pos) { - this.cell(pos.x, pos.y).push(entity); + push(this.cell(pos.x, pos.y), entity); entity.coord = array(pos); }, // remove an entity from a cell remove(entity, pos) { - def c = this.cell(pos.x, pos.y); - def i = search(c, entity); - if (i != null) c.splice(i, 1); + this.cells[pos.y][pos.x] = filter(this.cells[pos.y][pos.x], x => x != entity) }, // bounds check diff --git a/examples/chess/pieces.cm b/examples/chess/pieces.cm index 8aa50140..e79c4dea 100644 --- a/examples/chess/pieces.cm +++ b/examples/chess/pieces.cm @@ -1,28 +1,27 @@ /* pieces.js – simple data holders + starting layout */ function Piece(kind, colour) { - this.kind = kind; // "pawn" etc. - this.colour = colour; // "white"/"black" - this.sprite = colour + '_' + kind; // for draw2d.image - this.captured = false; - this.coord = [0,0]; + var newpiece = { kind, colour} + + newpiece.sprite = colour + '_' + kind; // for draw2d.image + newpiece.captured = false; + newpiece.coord = [0,0]; + + return newpiece } -Piece.prototype.toString = function () { - return character(this.colour) + upper(character(this.kind)); -}; function startingPosition(grid) { var W = 'white', B = 'black', x; // pawns for (x = 0; x < 8; x++) { - grid.add(new Piece('pawn', W), [x, 6]); - grid.add(new Piece('pawn', B), [x, 1]); + grid.add(Piece('pawn', W), [x, 6]); + grid.add(Piece('pawn', B), [x, 1]); } // major pieces var back = ['rook','knight','bishop','queen','king','bishop','knight','rook']; for (x = 0; x < 8; x++) { - grid.add(new Piece(back[x], W), [x, 7]); - grid.add(new Piece(back[x], B), [x, 0]); + grid.add(Piece(back[x], W), [x, 7]); + grid.add(Piece(back[x], B), [x, 0]); } } diff --git a/examples/snake/main.ce b/examples/snake/main.ce index 03a92eab..09e32d9f 100644 --- a/examples/snake/main.ce +++ b/examples/snake/main.ce @@ -75,7 +75,7 @@ this.update = function(dt) { // Eat apple? if (head.x == apple.x && head.y == apple.y) spawnApple() - else snake.pop() + else pop(snake) } this.hud = function() { diff --git a/examples/tetris/main.ce b/examples/tetris/main.ce index 8955a83a..7a376809 100644 --- a/examples/tetris/main.ce +++ b/examples/tetris/main.ce @@ -58,8 +58,8 @@ function initBoard() { board = [] for (var r=0; r cell)) { lines++ // remove row - board.splice(r,1) + board = array(array(board, 0, r), array(board, r+1)) // add empty row on top var newRow = [] - for (var c=0; c { - if (length(group_index[g]) > 0) groups.push(g) + if (length(group_index[g]) > 0) push(groups, g) }) return groups } @@ -282,7 +285,7 @@ film2d.render = function(params, render_backend) { b = [] buckets[layer_key] = b } - b.push(d) + push(b, d) } // 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 } - 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 var commands = [] - commands.push({ cmd: "begin_render", target: target, clear: clear_color, target_size: target_size }) - commands.push({ cmd: "set_camera", camera: camera }) + push(commands, { cmd: "begin_render", target: target, clear: clear_color, target_size: target_size }) + push(commands, { cmd: "set_camera", camera: camera }) var batches = _batch_drawables(drawables) for (var i = 0; i < length(batches); i++) { var batch = batches[i] 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") - 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") - commands.push({ cmd: "draw_text", drawable: batch.drawable }) + push(commands, { cmd: "draw_text", drawable: batch.drawable }) 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") - 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 } } @@ -350,9 +353,9 @@ function _batch_drawables(drawables) { var mat = d.material || {blend: 'alpha', sampler: d.filter || 'nearest'} if (current && current.type == 'sprite_batch' && current.texture == tex && _mat_eq(current.material, mat)) { - current.sprites.push(d) + push(current.sprites, d) } else { - if (current) batches.push(current) + if (current) push(batches, current) current = {type: 'sprite_batch', texture: tex, material: mat, sprites: [d]} } } 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)) { - current.sprites.push(sprite) + push(current.sprites, sprite) } else { - if (current) batches.push(current) + if (current) push(batches, current) current = {type: 'sprite_batch', texture: tex, material: mat, sprites: [sprite]} } } @@ -422,9 +425,9 @@ function _batch_drawables(drawables) { var tex = img var mat = default_mat if (current && current.type == 'sprite_batch' && current.texture == tex && _mat_eq(current.material, mat)) { - current.sprites.push(sprite) + push(current.sprites, sprite) } else { - if (current) batches.push(current) + if (current) push(batches, current) 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'} if (current && current.type == 'mesh2d_batch' && current.texture == tex && _mat_eq(current.material, mat)) { - current.meshes.push(d) + push(current.meshes, d) } else { - if (current) batches.push(current) + if (current) push(batches, current) current = {type: 'mesh2d_batch', texture: tex, material: mat, meshes: [d]} } } else if (d.type == 'shape') { // Shapes are rendered individually (each has unique SDF params) if (current) { - batches.push(current) + push(batches, current) current = null } - batches.push({type: 'shape', drawable: d}) + push(batches, {type: 'shape', drawable: d}) } else { if (current) { - batches.push(current) + push(batches, current) 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 } diff --git a/fx_graph.cm b/fx_graph.cm index 47e0310d..c87584fb 100644 --- a/fx_graph.cm +++ b/fx_graph.cm @@ -67,7 +67,7 @@ fx_graph.add_node = function(type, params) { params: params, output: {node_id: this.next_id - 1, slot: 'output'} } - this.nodes.push(node) + push(this.nodes, node) return node } @@ -203,7 +203,7 @@ NODE_EXECUTORS.composite = function(params, backend) { // Emit composite_textures command (handled outside render pass) var commands = [] - commands.push({ + push(commands, { cmd: 'composite_textures', base: base.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) var commands = [] - commands.push({ + push(commands, { cmd: 'apply_mask', content_texture: content.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 for (var i = length(commands) - 1; i >= 0; i--) { 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 } } @@ -305,7 +305,7 @@ NODE_EXECUTORS.blit = function(params, backend) { } var commands = [] - commands.push({ + push(commands, { cmd: 'blit', texture: src_target, target: target, @@ -321,7 +321,7 @@ NODE_EXECUTORS.present = function(params, backend) { var input = params.input var commands = [] - commands.push({cmd: 'present'}) + push(commands, {cmd: 'present'}) return {commands: commands} } @@ -350,7 +350,7 @@ NODE_EXECUTORS.shader_pass = function(params, backend) { } var commands = [] - commands.push({ + push(commands, { cmd: 'shader_pass', shader: shader, input: src, diff --git a/gestures.cm b/gestures.cm index 4e877111..1c431d03 100644 --- a/gestures.cm +++ b/gestures.cm @@ -41,7 +41,7 @@ gesture.on_input = function(action_id, action) { } else if (touchCount == 2) { // Two touches - potential pinch 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]) } } @@ -55,7 +55,7 @@ gesture.on_input = function(action_id, action) { if (touchCount == 2 && this.gestureState == 'multi') { // 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 d = currentDist - this.startDist diff --git a/graphics.cm b/graphics.cm index c92d4a7e..d718c520 100644 --- a/graphics.cm +++ b/graphics.cm @@ -154,7 +154,7 @@ function create_image(path){ try{ def bytes = io.slurp(path); - var ext = array(path, '.').pop() + var ext = pop(array(path, '.')) var raw = decode_image(bytes, ext); /* ── Case A: single surface (from make_texture) ────────────── */ @@ -357,7 +357,7 @@ graphics.texture = function texture(path) { } 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 if (!(basename in cache)) return diff --git a/input.cm b/input.cm index 98bc926e..7e05aa9d 100644 --- a/input.cm +++ b/input.cm @@ -93,13 +93,13 @@ function create_user(index, config) { // Push entity onto control stack push: function(entity) { - this.control_stack.push(entity) + push(this.control_stack, entity) }, // Pop from control stack pop: function() { if (length(this.control_stack) > 1) { - return this.control_stack.pop() + return pop(this.control_stack) } return null }, @@ -166,7 +166,7 @@ function pick_user(canon) { user.active_device = canon.device_id 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 _users = [] for (var i = 0; i < _config.max_users; i++) { - _users.push(create_user(i, _config)) + push(_users, create_user(i, _config)) } _initialized = true diff --git a/input/bindings.cm b/input/bindings.cm index cc44e141..7aac9be3 100644 --- a/input/bindings.cm +++ b/input/bindings.cm @@ -135,7 +135,8 @@ function make(defaults, display_names) { // Remove from other actions arrfor(array(this.action_map), act => { 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 @@ -143,9 +144,8 @@ function make(defaults, display_names) { for (var i = length(target) - 1; i >= 0; i--) { var existing_kind = starts_with(target[i], 'gamepad_') ? 'gamepad' : starts_with(target[i], 'swipe_') ? 'touch' : 'keyboard' - if (existing_kind == device_kind) { - target.splice(i, 1) - } + if (existing_kind == device_kind) + this.action_map[action] = array(array(this.action_map[action], 0, i), array(this.action_map[action], i+1)) } // Add new binding diff --git a/input/router.cm b/input/router.cm index 1189ffbb..606c4b40 100644 --- a/input/router.cm +++ b/input/router.cm @@ -40,7 +40,7 @@ function gesture_stage(config) { // Only process gamepad touchpad events if (ev.control != 'gamepad_touchpad') { - output.push(ev) + push(output, ev) continue } @@ -72,7 +72,7 @@ function gesture_stage(config) { var d = currentDist - start_dist if (Math.abs(d) >= pinch_th / 100) { - output.push({ + push(output, { kind: 'gesture', device_id: ev.device_id, control: d > 0 ? 'pinch_out' : 'pinch_in', @@ -103,7 +103,7 @@ function gesture_stage(config) { ? (dx > 0 ? 'swipe_right' : 'swipe_left') : (dy > 0 ? 'swipe_down' : 'swipe_up') - output.push({ + push(output, { kind: 'gesture', device_id: ev.device_id, control: dir, @@ -139,18 +139,18 @@ function emacs_stage() { // Only process keyboard button events if (ev.device_id != 'kbm' || ev.kind != 'button' || !ev.pressed) { - output.push(ev) + push(output, ev) continue } if (find(valid_emacs_keys, ev.control) == null) { - output.push(ev) + push(output, ev) continue } // Only process if we have modifiers OR waiting for chord if (!ev.mods?.ctrl && !ev.mods?.alt && !prefix) { - output.push(ev) + push(output, ev) continue } @@ -174,7 +174,7 @@ function emacs_stage() { if (prefix) { var chord = prefix + " " + notation prefix = null - output.push({ + push(output, { kind: 'chord', device_id: ev.device_id, control: chord, @@ -183,7 +183,7 @@ function emacs_stage() { time: ev.time }) } else { - output.push({ + push(output, { kind: 'chord', device_id: ev.device_id, control: notation, @@ -213,14 +213,14 @@ function action_stage(bindings) { // Pass through non-button events if (ev.kind != 'button' && ev.kind != 'chord' && ev.kind != 'gesture') { - output.push(ev) + push(output, ev) continue } var actions = bindings.get_actions(ev.control) if (length(actions) == 0) { - output.push(ev) + push(output, ev) continue } @@ -230,7 +230,7 @@ function action_stage(bindings) { if (ev.pressed) down[action] = true else if (ev.released) down[action] = false - output.push({ + push(output, { kind: 'action', device_id: ev.device_id, control: action, @@ -277,16 +277,16 @@ function make(user, config) { var action = null if (config.gestures != false) { - stages.push(gesture_stage(config)) + push(stages, gesture_stage(config)) } if (config.emacs != false) { - stages.push(emacs_stage()) + push(stages, emacs_stage()) } action = action_stage(user.bindings) - stages.push(action) - stages.push(delivery_stage(user)) + push(stages, action) + push(stages, delivery_stage(user)) return { stages: stages, diff --git a/line2d.cm b/line2d.cm index 45ac4af1..c3f533db 100644 --- a/line2d.cm +++ b/line2d.cm @@ -81,9 +81,9 @@ function build_polyline_mesh(line) { for (var i = 0; i < length(points); i++) { var p = points[i] 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 { - 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++) { var dx = pts[i].x - pts[i-1].x 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] @@ -179,7 +179,7 @@ function build_polyline_mesh(line) { n.y = dx } - normals.push(n) + push(normals, n) } // Generate vertices (2 per point - left and right of line) @@ -190,7 +190,7 @@ function build_polyline_mesh(line) { var u = get_u(i) // Left vertex (v=0) - verts.push({ + push(verts, { x: p.x + n.x * w, y: p.y + n.y * w, u: u, @@ -199,7 +199,7 @@ function build_polyline_mesh(line) { }) // Right vertex (v=1) - verts.push({ + push(verts, { x: p.x - n.x * w, y: p.y - n.y * w, u: u, @@ -212,24 +212,24 @@ function build_polyline_mesh(line) { for (var i = 0; i < length(pts) - 1; i++) { var base = i * 2 // First triangle - indices.push(base + 0) - indices.push(base + 1) - indices.push(base + 2) + push(indices, base + 0) + push(indices, base + 1) + push(indices, base + 2) // Second triangle - indices.push(base + 1) - indices.push(base + 3) - indices.push(base + 2) + push(indices, base + 1) + push(indices, base + 3) + push(indices, base + 2) } // Handle closed path if (closed && length(pts) > 2) { var last = (length(pts) - 1) * 2 - indices.push(last + 0) - indices.push(last + 1) - indices.push(0) - indices.push(last + 1) - indices.push(1) - indices.push(0) + push(indices, last + 0) + push(indices, last + 1) + push(indices, 0) + push(indices, last + 1) + push(indices, 1) + push(indices, 0) } // 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 // Center vertex - verts.push({ + push(verts, { x: p.x, y: p.y, 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 cy = math.sine(angle) - verts.push({ + push(verts, { x: p.x + cx * w, y: p.y + cy * w, u: u, @@ -285,9 +285,9 @@ function add_round_cap(verts, indices, p, n, width, u, v_offset, v_scale, is_sta // Fan triangles for (var i = 0; i < segments; i++) { - indices.push(base_idx) - indices.push(base_idx + 1 + i) - indices.push(base_idx + 2 + i) + push(indices, base_idx) + push(indices, base_idx + 1 + 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 // 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}) - 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}) - 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}) - 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: 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_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_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) - indices.push(base_idx + 1) - indices.push(base_idx + 2) - indices.push(base_idx + 1) - indices.push(base_idx + 3) - indices.push(base_idx + 2) + push(indices, base_idx + 0) + push(indices, base_idx + 1) + push(indices, base_idx + 2) + push(indices, base_idx + 1) + push(indices, base_idx + 3) + push(indices, base_idx + 2) } var defaults = { diff --git a/particles2d.cm b/particles2d.cm index 101499e0..a392c323 100644 --- a/particles2d.cm +++ b/particles2d.cm @@ -18,7 +18,7 @@ var particles2d_proto = { var emitters = { // Spawn a particle for an emitter spawn: function(emitter) { - emitter.particles.push({ + push(emitter.particles, { pos: { x: emitter.pos.x + (random() - 0.5) * emitter.spawn_area.width, 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) } 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 - if (emitter.handle) { + if (emitter.handle) emitter.handle.particles = emitter.particles - } }, // Create an emitter config diff --git a/playdate.cm b/playdate.cm index 0e53bec9..cbc60920 100644 --- a/playdate.cm +++ b/playdate.cm @@ -56,7 +56,7 @@ PlaydateBackend.prototype.get_or_create_target = function(width, height, key) { key: key } - this.target_pool[pool_key].push(target) + push(this.target_pool[pool_key], target) return target } diff --git a/rasterize.cm b/rasterize.cm index f6e98abe..d85f3621 100644 --- a/rasterize.cm +++ b/rasterize.cm @@ -91,7 +91,7 @@ rasterize.ellipse = function ellipse(pos, radii, opt) { within_wedge(dx+1, dy, start, end, full_circle) if (last || !next_in_ring) { - strips.push({ + push(strips, { x: run_start, y: y_screen, 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 if (w <= 0) continue - strips.push( + push(strips, { 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_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 w = (dx << 1) + 1 - caps.push( + push(caps, { 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_l - dx, y:cy_b + dy, width:w, height:1 }, diff --git a/resources.cm b/resources.cm index 4ba07397..906f7cdf 100644 --- a/resources.cm +++ b/resources.cm @@ -100,7 +100,7 @@ function read_ignore(dir) { arrfor(lines, function(line) { line = trim(line) if (!line || starts_with(line, '#')) return - patterns.push(line) + push(patterns, line) }) } return patterns @@ -120,7 +120,7 @@ Resources.getAllFiles = function(dir = "") { if (!st.filesize) return var ext = getExtension(f) if (!isRecognizedExtension(ext)) return - results.push(fullPath) + push(results, fullPath) } catch(e) {} }) return results diff --git a/sdl_gpu.cm b/sdl_gpu.cm index e8faa215..f17940f8 100644 --- a/sdl_gpu.cm +++ b/sdl_gpu.cm @@ -700,7 +700,7 @@ function _load_image_file(path) { var decoded if (!bytes) return null - var ext = lower(array(path, '.').pop()) + var ext = lower(pop(array(path, '.'))) var surface = null switch (ext) { @@ -822,7 +822,7 @@ sdl_gpu.get_or_create_target = function(width, height, key) { key: key } - _target_pool[pool_key].push(target) + push(_target_pool[pool_key], target) return target } @@ -1202,23 +1202,23 @@ function _execute_commands(commands, window_size) { break case 'draw_batch': - pending_draws.push(cmd) + push(pending_draws, cmd) break case 'draw_text': - pending_draws.push(cmd) + push(pending_draws, cmd) break case 'draw_texture_ref': - pending_draws.push(cmd) + push(pending_draws, cmd) break case 'draw_shape': - pending_draws.push(cmd) + push(pending_draws, cmd) break case 'draw_mesh2d': - pending_draws.push(cmd) + push(pending_draws, cmd) break case 'blit': @@ -2219,7 +2219,7 @@ function _do_shader_pass(cmd_buffer, cmd, get_swapchain_tex) { var samplers = [{texture: input.texture, sampler: _sampler_linear}] if (cmd.extra_inputs) { 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) diff --git a/tests/bunnymark.ce b/tests/bunnymark.ce index 77a63f79..9cf7e30c 100644 --- a/tests/bunnymark.ce +++ b/tests/bunnymark.ce @@ -87,16 +87,6 @@ function loop() render.present() 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 $delay(loop, delay) } diff --git a/tests/camera_colorspace.ce b/tests/camera_colorspace.ce index 33af2e75..5e481059 100644 --- a/tests/camera_colorspace.ce +++ b/tests/camera_colorspace.ce @@ -23,7 +23,7 @@ for (var i = 0; i < length(formats); i++) { if (!colorspaces[fmt.colorspace]) { colorspaces[fmt.colorspace] = []; } - colorspaces[fmt.colorspace].push(fmt); + push(colorspaces[fmt.colorspace], fmt); } log.console("\nFound colorspaces:"); diff --git a/tests/surface.ce b/tests/surface.ce index f2c0063f..527393cb 100644 --- a/tests/surface.ce +++ b/tests/surface.ce @@ -2,7 +2,7 @@ var Surface = use('sdl3/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(surf) @@ -26,12 +26,12 @@ var pixels = surf.pixels(); log.console("Got pixels array buffer, length:", pixels.byteLength); // 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); // Test creating surface from pixels -var pixelData = new ArrayBuffer(32 * 32 * 4); // 32x32 RGBA -var surf5 = new Surface({width: 32, height: 32, pixels: pixelData}); +var pixelData = ArrayBuffer(32 * 32 * 4); // 32x32 RGBA +var surf5 = Surface({width: 32, height: 32, pixels: pixelData}); log.console("Created surface from pixels:", surf5.width, "x", surf5.height); log.console("Surface module test passed!"); \ No newline at end of file diff --git a/tilemap2d.cm b/tilemap2d.cm index e0fca815..167ee5f5 100644 --- a/tilemap2d.cm +++ b/tilemap2d.cm @@ -19,10 +19,10 @@ var tilemap = { var y = pos.y - this.offset_y while (length(this.tiles) <= x) - this.tiles.push([]) + push(this.tiles, []) while (length(this.tiles[x]) <= y) - this.tiles[x].push(null) + push(this.tiles[x], null) this.tiles[x][y] = image }, diff --git a/tween.cm b/tween.cm index 8a70eceb..47b55409 100644 --- a/tween.cm +++ b/tween.cm @@ -8,7 +8,7 @@ function make_engine(default_clock) { tweens: [], default_clock: default_clock || null, add(tween) { - this.tweens.push(tween) + push(this.tweens, tween) }, remove(tween) { this.tweens = filter(this.tweens, t => t != tween) @@ -160,7 +160,7 @@ var TimelineProto = { engine: null, 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) {