bezier spline controls

This commit is contained in:
John Alanbrook 2023-12-18 12:45:27 +00:00
parent b732d12bed
commit 444fb98125
12 changed files with 309 additions and 172 deletions

View file

@ -3,16 +3,16 @@
Register functions with built-in drawing callbacks to have code execute at particular points in time. Some drawing functions take world coordinates, while others take screen coordinates. Register functions with built-in drawing callbacks to have code execute at particular points in time. Some drawing functions take world coordinates, while others take screen coordinates.
gui gui
Called before nk_gui. Screen space. Called every frame.
draw draw
Called every frame. World space. Called every frame.
debug debug
Called if drawing physics. World space. Called if drawing physics.
gizmo gizmo
Called usually for editor purposes. World space. Called on an object if it is selected in the editor.
# Mum # Mum

View file

@ -110,6 +110,7 @@
/* test the GC by forcing it before each object allocation */ /* test the GC by forcing it before each object allocation */
//#define FORCE_GC_AT_MALLOC //#define FORCE_GC_AT_MALLOC
#ifdef CONFIG_ATOMICS #ifdef CONFIG_ATOMICS
#include <pthread.h> #include <pthread.h>
#include <stdatomic.h> #include <stdatomic.h>

View file

@ -1,5 +1,3 @@
/* Removing crud I don't like */
/* Functions that lead to bloated error prone javascript */
/* It is EMCA6 but without a lot of builtin objects and various functions. There are no: /* It is EMCA6 but without a lot of builtin objects and various functions. There are no:
* Promises and so on (Generators, async) * Promises and so on (Generators, async)
* WeakMaps and so on (weakset, weakref) * WeakMaps and so on (weakset, weakref)
@ -10,7 +8,7 @@
In addition to the removal of a bunch of stuff as seen here. In addition to the removal of a bunch of stuff as seen here.
Access prototypes through __proto__ instead of the long-winded Object.getProtoTypeOf. Access prototypes through __proto__ instead of the long-winded Object.getProtoTypeOf.
*/ */
/*
Object.getPrototypeOf = undefined; Object.getPrototypeOf = undefined;
Object.setPrototypeOf = undefined; Object.setPrototypeOf = undefined;
Reflect = undefined; Reflect = undefined;
@ -22,6 +20,7 @@ WeakMap = undefined;
Promise = undefined; Promise = undefined;
Set = undefined; Set = undefined;
WeakSet = undefined; WeakSet = undefined;
*/
var fmt = {}; var fmt = {};
@ -1127,7 +1126,7 @@ Object.defineProperty(Array.prototype, 'find', {
}}); }});
Object.defineProperty(Array.prototype, 'last', { Object.defineProperty(Array.prototype, 'last', {
get: function() { return this[this.length-1]; }, value: function() { return this[this.length-1]; },
}); });
Object.defineProperty(Array.prototype, 'at', { Object.defineProperty(Array.prototype, 'at', {

View file

@ -487,7 +487,7 @@ component.polygon2d = Object.copy(collider2d, {
gizmo() { gizmo() {
this.spoints.forEach(function(x) { this.spoints.forEach(function(x) {
Debug.point(world2screen(this.gameobject.this2world(x)), 3, Color.green); Shape.point(world2screen(this.gameobject.this2world(x)), 3, Color.green);
}, this); }, this);
this.points.forEach(function(x, i) { this.points.forEach(function(x, i) {
@ -499,7 +499,8 @@ component.polygon2d = Object.copy(collider2d, {
if (!Object.hasOwn(this,'points')) if (!Object.hasOwn(this,'points'))
this.points = deep_copy(this.__proto__.points); this.points = deep_copy(this.__proto__.points);
var p = Gizmos.pick_gameobject_points(pos, this.gameobject, this.points); var i = Gizmos.pick_gameobject_points(pos, this.gameobject, this.points);
var p = this.points[i];
if (p) if (p)
return make_point_obj(this, p); return make_point_obj(this, p);
@ -553,6 +554,7 @@ component.edge2d = Object.copy(collider2d, {
thickness:0, thickness:0,
/* if type === -1, point to point */ /* if type === -1, point to point */
type: Spline.type.catmull, type: Spline.type.catmull,
C: 1, /* when in bezier, continuity required. 0, 1 or 2. */
looped: false, looped: false,
angle: 3, /* maximum angle between two segments */ angle: 3, /* maximum angle between two segments */
@ -612,30 +614,18 @@ component.edge2d = Object.copy(collider2d, {
} }
if (this.type === Spline.type.catmull) { if (this.type === Spline.type.catmull) {
if (this.looped) { if (this.looped)
spoints.unshift(spoints[spoints.length-1]); spoints = Spline.catmull_loop(spoints);
spoints.push(spoints[1]); else
spoints.push(spoints[2]); spoints = Spline.catmull_caps(spoints);
} else {
spoints.unshift(spoints[0].sub(spoints[1]).add(spoints[0]));
spoints.push(spoints[spoints.length-1].sub(spoints[spoints.length-2]).add(spoints[spoints.length-1]));
}
return Spline.sample_angle(this.type, spoints,this.angle); return Spline.sample_angle(this.type, spoints,this.angle);
} }
var tp = [];
for (var i = 0; i < spoints.length-1; i++)
tp.push([spoints[i+1].sub(spoints[i]), spoints[i+1].sub(spoints[i])]);
spoints = spoints.map((p,i) => p.concat(tp[i]));
spoints = spoints.flat();
return Spline.sample_angle(this.type, spoints, this.angle); return Spline.sample_angle(this.type, spoints, this.angle);
}, },
boundingbox() { boundingbox() { return points2bb(this.points.map(x => x.scale(this.gameobject.scale))); },
return points2bb(this.points.map(x => x.scale(this.gameobject.scale)));
},
hides: ['gameobject', 'id', 'shape'], hides: ['gameobject', 'id', 'shape'],
_enghook: make_edge2d, _enghook: make_edge2d,
@ -643,27 +633,50 @@ component.edge2d = Object.copy(collider2d, {
/* EDITOR */ /* EDITOR */
gizmo() { gizmo() {
if (this.type === Spline.type.catmull) { if (this.type === Spline.type.catmull) {
this.spoints().forEach(x => Debug.point(this.gameobject.this2world(x), 3, Color.teal)); this.spoints().forEach(x => Shape.point(this.gameobject.this2screen(x), 3, Color.teal));
this.cpoints.forEach((x,i) => Debug.numbered_point(this.gameobject.this2world(x), i)); this.cpoints.forEach((x,i) => Debug.numbered_point(this.gameobject.this2screen(x), i));
} else { } else {
for (var i = 1; i < this.cpoints.length; i+=2) { for (var i = 0; i < this.cpoints.length; i += 3)
Debug.point(this.gameobject.this2world(this.cpoints[i]), 3, Color.green); Debug.numbered_point(this.gameobject.this2screen(this.cpoints[i]), i, Color.teal);
Debug.line([this.gameobject.this2world(this.cpoints[i-1]), this.gameobject.this2world(this.cpoints[i])], Color.yellow);
for (var i = 1; i < this.cpoints.length; i+=3) {
Debug.numbered_point(this.gameobject.this2screen(this.cpoints[i]), i, Color.green);
Debug.numbered_point(this.gameobject.this2screen(this.cpoints[i+1]), i+1, Color.green);
Shape.line([this.gameobject.this2screen(this.cpoints[i-1]), this.gameobject.this2screen(this.cpoints[i])], Color.yellow);
Shape.line([this.gameobject.this2screen(this.cpoints[i+1]), this.gameobject.this2screen(this.cpoints[i+2])], Color.yellow);
} }
// for (var i = 0; i < this.cpoints.length; i+=3)
// Debug.numbered_point(this.gameobject.this2world(this.cpoints[
} }
}, },
finish_center(change) { finish_center(change) { this.cpoints = this.cpoints.map(function(x) { return x.sub(change); }); },
this.cpoints = this.cpoints.map(function(x) { return x.sub(change); });
},
pick(pos) { pick(pos) {
var p = Gizmos.pick_gameobject_points(pos, this.gameobject, this.cpoints); var i = Gizmos.pick_gameobject_points(pos, this.gameobject, this.cpoints);
if (p) var p = this.cpoints[i];
return make_point_obj(this, p); var that = this.gameobject;
var me = this;
if (p) {
var o = {
pos: p,
sync: me.sync.bind(me)
};
if (Spline.bezier_is_handle(this.cpoints,i))
o.move = function(d) {
d = that.dir_world2this(d);
p.x += d.x;
p.y += d.y;
Spline.bezier_cp_mirror(me.cpoints,i);
};
else
o.move = function(d) {
d = that.dir_world2this(d);
p.x += d.x;
p.y += d.y;
var pp = Spline.bezier_point_handles(me.cpoints,i);
pp.forEach(ph => me.cpoints[ph] = me.cpoints[ph].add(d));
}
return o;
}
return undefined; return undefined;
}, },
@ -741,10 +754,25 @@ bucket.inputs['C-r'].doc = "Reverse the order of the spline's points.";
bucket.inputs['C-l'] = function() { this.looped = !this.looped}; bucket.inputs['C-l'] = function() { this.looped = !this.looped};
bucket.inputs['C-l'].doc = "Toggle spline being looped."; bucket.inputs['C-l'].doc = "Toggle spline being looped.";
bucket.inputs['C-c'] = function() { this.type = Spline.type.catmull; }; bucket.inputs['C-c'] = function() {
switch(this.type) {
case Spline.type.bezier:
this.cpoints = Spline.bezier2catmull(this.cpoints);
break;
}
this.type = Spline.type.catmull;
};
bucket.inputs['C-c'].doc = "Set type of spline to catmull-rom."; bucket.inputs['C-c'].doc = "Set type of spline to catmull-rom.";
bucket.inputs['C-b'] = function() { this.type = Spline.type.bezier; }; bucket.inputs['C-b'] = function() {
switch(this.type) {
case Spline.type.catmull:
this.cpoints = Spline.catmull2bezier(Spline.catmull_caps(this.cpoints));
break;
}
this.type = Spline.type.bezier;
};
bucket.inputs['C-o'] = function() { this.type = -1; }; bucket.inputs['C-o'] = function() { this.type = -1; };
bucket.inputs['C-o'].doc = "Set spline to linear."; bucket.inputs['C-o'].doc = "Set spline to linear.";
@ -760,10 +788,10 @@ bucket.inputs['C-M-lm'].doc = "Select the given point as the '0' of this spline.
bucket.inputs['C-lm'] = function() { bucket.inputs['C-lm'] = function() {
var idx = 0; var idx = 0;
if (this.cpoints.length >= 2) { if (this.cpoints.length >= 2)
idx = cmd(59, screen2world(Mouse.pos).sub(this.gameobject.pos), this.cpoints, 1000); idx = cmd(59, screen2world(Mouse.pos).sub(this.gameobject.pos), this.cpoints, 400);
if (idx === -1) return;
} console.say("new point");
if (idx === this.cpoints.length) if (idx === this.cpoints.length)
this.cpoints.push(this.gameobject.world2this(screen2world(Mouse.pos))); this.cpoints.push(this.gameobject.world2this(screen2world(Mouse.pos)));
@ -836,30 +864,10 @@ component.circle2d.impl = Object.mix({
/* ASSETS */ /* ASSETS */
var Texture = { var Texture = {
mipmaps(path, x) { mipmaps(path, x) { cmd(94, path, x); },
cmd(94, path, x);
},
sprite(path, x) { sprite(path, x) { cmd(95, path, x); },
cmd(95, path, x);
},
}; };
var Resources = { Texture.mipmaps.doc = "Return true if the texture has mipmaps.";
load(path) { Texture.sprite.do = "Return true if the texture is treated as a sprite.";
if (path in this)
return this[path];
var src = {};
this[path] = src;
src.path = path;
if (!IO.exists(`${path}.asset`))
return this[path];
var data = JSON.parse(IO.slurp(`${path}.asset`));
Object.assign(src,data);
return this[path];
},
};

View file

@ -1,13 +1,35 @@
var Gizmos = { /* All draw in screen space */
pick_gameobject_points(worldpos, gameobject, points) {
var idx = Math.grab_from_points(worldpos, points.map(gameobject.this2world,gameobject), 25);
if (idx === -1) return undefined;
return points[idx];
},
};
var Shape = { var Shape = {
circle(pos, radius, color) { cmd(115, pos, radius, color); }, circle(pos, radius, color) { cmd(115, pos, radius, color); },
point(pos,size,color) {
color ??= Color.blue;
Shape.circle(pos,size,color);
},
arrow(start, end, color, capsize) {
color ??= Color.red;
capsize ??= 4;
cmd(81, start, end, color, capsize);
},
poly(points, color) { cmd_points(0,points,color); },
box(pos, wh, color) {
color ??= Color.white;
cmd(53, pos, wh, color);
},
line(points, color, type, thickness) {
thickness ??= 1;
type ??= 0;
color ??= Color.white;
switch (type) {
case 0:
cmd(83, points, color, thickness);
}
},
}; };
var Debug = { var Debug = {
@ -27,38 +49,17 @@ var Debug = {
cmd(47, width, span, color); cmd(47, width, span, color);
}, },
point(pos, size, color) {
color = color ? color : Color.blue;
Shape.circle(pos, size, color);
// cmd(51, pos, size,color);
},
coordinate(pos, size, color) { GUI.text(JSON.stringify(pos.map(p=>Math.round(p))), pos, size, color); }, coordinate(pos, size, color) { GUI.text(JSON.stringify(pos.map(p=>Math.round(p))), pos, size, color); },
arrow(start, end, color, capsize) {
color = color ? color : Color.red;
if (!capsize)
capsize = 4;
cmd(81, start, end, color, capsize);
},
poly(points, color) {
cmd_points(0,points,color);
},
boundingbox(bb, color) { boundingbox(bb, color) {
color ??= Color.white; color ??= Color.white;
cmd_points(0, bb2points(bb), color); cmd_points(0, bb2points(bb), color);
}, },
box(pos, wh, color) { numbered_point(pos, n, color) {
color ??= Color.white; color ??= Color.white;
cmd(53, pos, wh, color); Shape.point(pos, 3, color);
}, GUI.text(n, pos.add([0,4]), 1, color);
numbered_point(pos, n) {
Debug.point(pos, 3);
GUI.text(n, pos.add([0,4]), 1);
}, },
phys_drawing: false, phys_drawing: false,
@ -75,22 +76,7 @@ var Debug = {
Register.debug.register(fn,obj); Register.debug.register(fn,obj);
}, },
gameobject(go) { gameobject(go) { cmd(15, go.body); },
},
line(points, color, type, thickness) {
thickness ??= 1;
type ??= 0;
if (!color)
color = Color.white;
switch (type) {
case 0:
cmd(83, points, color, thickness);
}
},
draw_bb: false, draw_bb: false,
draw_gizmos: false, draw_gizmos: false,
@ -136,8 +122,8 @@ Debug.Options.Color = {
var Gizmos = { var Gizmos = {
pick_gameobject_points(worldpos, gameobject, points) { pick_gameobject_points(worldpos, gameobject, points) {
var idx = Math.grab_from_points(worldpos, points.map(gameobject.this2world,gameobject), 25); var idx = Math.grab_from_points(worldpos, points.map(gameobject.this2world,gameobject), 25);
if (idx === -1) return null; if (idx === -1) return undefined;
return points[idx]; return idx;
}, },
}; };
@ -146,19 +132,51 @@ var Profile = {
ns(ticks) { return cmd(128, ticks); }, ns(ticks) { return cmd(128, ticks); },
us(ticks) { return cmd(129, ticks); }, us(ticks) { return cmd(129, ticks); },
ms(ticks) { return cmd(130, ticks); }, ms(ticks) { return cmd(130, ticks); },
best_t(ns) {
var e = ns;
var qq = 'ns';
if (e > 1000) {
e /= 1000;
qq = 'us';
if (e > 1000) {
e /= 1000;
qq = 'ms';
}
}
return {
time: e,
unit: qq
};
},
cpu(fn, times, q) { cpu(fn, times, q) {
times ??= 1; times ??= 1;
q ??= "ns"; q ??= "unnamed";
var start = Profile.tick_now(); var start = Profile.tick_now();
for (var i = 0; i < times; i++) for (var i = 0; i < times; i++)
fn(); fn();
var elapsed = Profile.tick_now() - start; var elapsed = Profile.tick_now() - start;
Log.say(`Profiled in ${Profile[q](elapsed)/times} avg ${q}.`); var avgt = Profile.best_t(elapsed/times);
var totalt = Profile.best_t(elapsed);
console.say(`Profile [${q}]: ${avgt.time.toFixed(3)} ${avgt.unit} average [${totalt.time.toFixed(3)} ${totalt.unit} for ${times} loops]`);
}, },
get fps() { return sys_cmd(8); }, get fps() { return sys_cmd(8); },
}; };
Profile.test = {
barecall() { profile(0); },
unpack_num(n) { profile(1,n); },
unpack_array(n) { profile(2,n); },
pack_num() { profile(3); },
pack_string() { profile(6); },
unpack_string(s) { profile(4,s); },
unpack_32farr(a) { profile(5,a); },
};
Profile.cpu.doc = `Output the time it takes to do a given function n number of times. Provide 'q' as "ns", "us", or "ms" to output the time taken in the requested resolution.`; Profile.cpu.doc = `Output the time it takes to do a given function n number of times. Provide 'q' as "ns", "us", or "ms" to output the time taken in the requested resolution.`;
/* These controls are available during editing, and during play of debug builds */ /* These controls are available during editing, and during play of debug builds */

View file

@ -270,7 +270,7 @@ var editor = {
if (!dif) return; if (!dif) return;
if (this.snapshots.length !== 0) { if (this.snapshots.length !== 0) {
var ddif = ediff(dif, this.snapshots.last); var ddif = ediff(dif, this.snapshots.last());
if (!ddif) return; if (!ddif) return;
dif = ddif; dif = ddif;
} }
@ -344,7 +344,7 @@ var editor = {
root = root ? root + "." : root; root = root ? root + "." : root;
Object.entries(obj.objects).forEach(function(x) { Object.entries(obj.objects).forEach(function(x) {
var p = root + x[0]; var p = root + x[0];
GUI.text(p, world2screen(x[1].worldpos()), 1, editor.color_depths[depth]); GUI.text(p, x[1].screenpos(), 1, editor.color_depths[depth]);
editor.draw_objects_names(x[1], p, depth+1); editor.draw_objects_names(x[1], p, depth+1);
}); });
}, },
@ -367,14 +367,14 @@ var editor = {
color_depths: [], color_depths: [],
draw() { draw() {
Debug.point(world2screen(this.cursor), 2, Color.green); Shape.point(world2screen(this.cursor), 2, Color.green);
this.selectlist.forEach(x => { this.selectlist.forEach(x => {
if ('gizmo' in x && typeof x['gizmo'] === 'function' ) if ('gizmo' in x && typeof x['gizmo'] === 'function' )
x.gizmo(); x.gizmo();
}); });
Debug.line(bb2points(cwh2bb([0,0],[Game.native.x,Game.native.y])).wrapped(1), Color.yellow); Shape.line(bb2points(cwh2bb([0,0],[Game.native.x,Game.native.y])).wrapped(1), Color.yellow);
/* Draw selection box */ /* Draw selection box */
if (this.sel_start) { if (this.sel_start) {
@ -389,7 +389,7 @@ var editor = {
wh[1] = Math.abs(endpos[1] - this.sel_start[1]); wh[1] = Math.abs(endpos[1] - this.sel_start[1]);
var bb = cwh2bb(c,wh); var bb = cwh2bb(c,wh);
Debug.boundingbox(bb, Color.Editor.select.alpha(0.1)); Debug.boundingbox(bb, Color.Editor.select.alpha(0.1));
Debug.line(bb2points(bb).wrapped(1), Color.white); Shape.line(bb2points(bb).wrapped(1), Color.white);
} }
Debug.coordinate([0,0]); Debug.coordinate([0,0]);
@ -405,9 +405,7 @@ var editor = {
if (this.comp_info && this.sel_comp) if (this.comp_info && this.sel_comp)
GUI.text(Input.print_pawn_kbm(this.sel_comp,false), [100,700],1); GUI.text(Input.print_pawn_kbm(this.sel_comp,false), [100,700],1);
GUI.text("+", editor.edit_level.screenpos(), 1, Color.blue);
// GUI.text(editor.edit_level.worldpos().map(x => Math.round(x)), world2screen(editor.edit_level.worldpos()), 1, Color.red);
GUI.text("+", world2screen(editor.edit_level.worldpos()), 1, Color.blue);
var thiso = editor.get_this(); var thiso = editor.get_this();
var clvl = thiso; var clvl = thiso;
@ -454,21 +452,20 @@ var editor = {
x._ed.check_dirty(); x._ed.check_dirty();
if (x._ed.dirty) sname += "*"; if (x._ed.dirty) sname += "*";
GUI.text(sname, world2screen(x.worldpos()).add([0, 32]), 1, Color.editor.ur); GUI.text(sname, x.screenpos().add([0, 32]), 1, Color.editor.ur);
GUI.text(x.worldpos().map(function(x) { return Math.round(x); }), world2screen(x.worldpos()), 1, Color.white); GUI.text(x.worldpos().map(function(x) { return Math.round(x); }), x.screenpos(), 1, Color.white);
// Debug.arrow(world2screen(x.worldpos()), world2screen(x.worldpos().add(x.up().scale(40))), Color.yellow, 1);
}); });
Object.entries(thiso.objects).forEach(function(x) { Object.entries(thiso.objects).forEach(function(x) {
var p = x[1]._ed.namestr(); var p = x[1]._ed.namestr();
GUI.text(p, world2screen(x[1].worldpos().add([0,16])),1,editor.color_depths[depth]); GUI.text(p, x[1].screenpos().add([0,16]),1,editor.color_depths[depth]);
}); });
var mg = physics.pos_query(Mouse.worldpos); var mg = physics.pos_query(Mouse.worldpos);
if (mg) { if (mg) {
var p = mg.path_from(thiso); var p = mg.path_from(thiso);
GUI.text(p, world2screen(Mouse.worldpos),1,Color.teal); GUI.text(p, Mouse.screenpos(),1,Color.teal);
} }
if (this.selectlist.length === 1) { if (this.selectlist.length === 1) {
@ -476,7 +473,7 @@ var editor = {
for (var key in this.selectlist[0].components) { for (var key in this.selectlist[0].components) {
var selected = this.sel_comp === this.selectlist[0].components[key]; var selected = this.sel_comp === this.selectlist[0].components[key];
var str = (selected ? ">" : " ") + key + " [" + this.selectlist[0].components[key].toString() + "]"; var str = (selected ? ">" : " ") + key + " [" + this.selectlist[0].components[key].toString() + "]";
GUI.text(str, world2screen(this.selectlist[0].worldpos()).add([0,-16*(i++)])); GUI.text(str, this.selectlist[0].screenpos().add([0,-16*(i++)]));
} }
if (this.sel_comp) { if (this.sel_comp) {
@ -486,7 +483,7 @@ var editor = {
editor.edit_level.objects.forEach(function(obj) { editor.edit_level.objects.forEach(function(obj) {
if (!obj._ed.selectable) if (!obj._ed.selectable)
GUI.image("icons/icons8-lock-16.png", world2screen(obj.worldpos())); GUI.image("icons/icons8-lock-16.png", obj.screenpos());
}); });
Debug.draw_grid(1, editor.grid_size, Color.Editor.grid.alpha(0.3)); Debug.draw_grid(1, editor.grid_size, Color.Editor.grid.alpha(0.3));
@ -1684,7 +1681,7 @@ var objectexplorer = Object.copy(inputpanel, {
} }
if (!this.previous.empty) if (!this.previous.empty)
items.push(Mum.text({str:"prev: " + this.previous.last, action: this.prev_obj})); items.push(Mum.text({str:"prev: " + this.previous.last(), action: this.prev_obj}));
Object.getOwnPropertyNames(this.obj).forEach(key => { Object.getOwnPropertyNames(this.obj).forEach(key => {
var descriptor = Object.getOwnPropertyDescriptor(this.obj, key); var descriptor = Object.getOwnPropertyDescriptor(this.obj, key);

View file

@ -95,12 +95,11 @@ var timer = {
load("scripts/animation.js"); load("scripts/animation.js");
var Render = { var Render = {
normal() { normal() { cmd(67);},
cmd(67); wireframe() { cmd(68); },
},
pass() {
wireframe() {
cmd(68);
}, },
}; };
@ -125,7 +124,7 @@ function world2screen(worldpos) { return Game.camera.world2view(worldpos); }
var Register = { var Register = {
kbm_input(mode, btn, state, ...args) { kbm_input(mode, btn, state, ...args) {
if (state === 'released') { if (state === 'released') {
btn = btn.split('-').last; btn = btn.split('-').last();
} }
switch(mode) { switch(mode) {
@ -338,6 +337,45 @@ Spline.sample_angle = function(type, points, angle) {
return spline_cmd(0, type, points[0].length, points, angle); return spline_cmd(0, type, points[0].length, points, angle);
} }
Spline.bezier2catmull = function(b)
{
var c = [];
for (var i = 0; i < b.length; i += 3)
c.push(b[i]);
return c;
}
Spline.catmull2bezier = function(c)
{
var b = [];
for (var i = 1; i < c.length-2; i++) {
b.push(c[i].slice());
b.push(c[i+1].sub(c[i-1]).scale(0.25).add(c[i]));
b.push(c[i].sub(c[i+2]).scale(0.25).add(c[i+1]));
}
b.push(c[c.length-2]);
return b;
}
Spline.catmull_loop = function(cp)
{
cp = cp.slice();
cp.unshift(cp.last());
cp.push(cp[1]);
cp.push(cp[2]);
return cp;
}
Spline.catmull_caps = function(cp)
{
cp = cp.slice();
cp.unshift(cp[0].sub(cp[1]).add(cp[0]));
cp.push(cp.last().sub(cp.at(-2).add(cp.last())));
return cp;
}
Spline.catmull2bezier.doc = "Given a set of control points C for a camtull-rom type curve, return a set of cubic bezier points to give the same curve."
Spline.type = { Spline.type = {
catmull: 0, catmull: 0,
bezier: 1, bezier: 1,
@ -345,6 +383,40 @@ Spline.type = {
cubichermite: 3 cubichermite: 3
}; };
Spline.bezier_tan_partner = function(points, i)
{
if (i%3 === 0) return undefined;
var partner_i = (i%3) === 2 ? i-1 : i+1;
return points[i];
}
Spline.bezier_cp_mirror = function(points, i)
{
if (i%3 === 0) return undefined;
var partner_i = (i%3) === 2 ? i+2 : i-2;
var node_i = (i%3) === 2 ? i+1 : i-1;
if (partner_i >= points.length || node_i >= points.length) return;
points[partner_i] = points[node_i].sub(points[i]).add(points[node_i]);
}
Spline.bezier_point_handles = function(points, i)
{
if (!Spline.bezier_is_node(points,i)) return [];
var a = i-1;
var b = i+1;
var c = []
if (a > 0)
c.push(a);
if (b < points.length)
c.push(b);
return c;
}
Spline.bezier_is_node = function(points, i) { return i%3 === 0; }
Spline.bezier_is_handle = function(points, i) { return !Spline.bezier_is_node(points,i); }
load("scripts/components.js"); load("scripts/components.js");
var Game = { var Game = {

View file

@ -181,6 +181,7 @@ var gameobject = {
set_body(2,this.body,x); set_body(2,this.body,x);
this.objects.forEach((o,i) => o.set_worldpos(this.this2world(poses[i]))); this.objects.forEach((o,i) => o.set_worldpos(this.this2world(poses[i])));
}, },
screenpos() { return world2screen(this.worldpos()); },
worldangle() { return Math.rad2deg(q_body(2,this.body))%360; }, worldangle() { return Math.rad2deg(q_body(2,this.body))%360; },
sworldangle(x) { set_body(0,this.body,Math.deg2rad(x)); }, sworldangle(x) { set_body(0,this.body,Math.deg2rad(x)); },
@ -280,7 +281,8 @@ var gameobject = {
shove(vec) { set_body(12,this.body,vec);}, shove(vec) { set_body(12,this.body,vec);},
shove_at(vec, at) { set_body(14,this.body,vec,at); }, shove_at(vec, at) { set_body(14,this.body,vec,at); },
world2this(pos) { return cmd(70, this.body, pos); }, world2this(pos) { return cmd(70, this.body, pos); },
this2world(pos) { return cmd(71, this.body,pos); }, this2world(pos) { return cmd(71, this.body, pos); },
this2screen(pos) { return world2screen(this.this2world(pos)); },
dir_world2this(dir) { return cmd(160, this.body, dir); }, dir_world2this(dir) { return cmd(160, this.body, dir); },
dir_this2world(dir) { return cmd(161, this.body, dir); }, dir_this2world(dir) { return cmd(161, this.body, dir); },

View file

@ -1,3 +1,7 @@
/*
GUI functions take screen space coordinates
*/
var GUI = { var GUI = {
text(str, pos, size, color, wrap, anchor, cursor) { text(str, pos, size, color, wrap, anchor, cursor) {
size ??= 1; size ??= 1;
@ -271,7 +275,7 @@ GUI.window = function(pos, wh, color)
var p = pos.slice(); var p = pos.slice();
p.x += wh.x/2; p.x += wh.x/2;
p.y += wh.y/2; p.y += wh.y/2;
Debug.box(p,wh,color); Shape.box(p,wh,color);
} }
GUI.flush = function() { cmd(141); }; GUI.flush = function() { cmd(141); };

View file

@ -4,9 +4,9 @@ var Input = {
}; };
var Mouse = { var Mouse = {
get pos() { get pos() { return cmd(45); },
return cmd(45);
}, screenpos() { return cmd(45); },
get worldpos() { get worldpos() {
return screen2world(cmd(45)); return screen2world(cmd(45));

View file

@ -69,10 +69,7 @@ JS_NewClass(JS_GetRuntime(js), js_##TYPE##_id, &js_##TYPE##_class);\
(byte & 0x02 ? '1' : '0'), \ (byte & 0x02 ? '1' : '0'), \
(byte & 0x01 ? '1' : '0') (byte & 0x01 ? '1' : '0')
void js_setprop_str(JSValue obj, const char *prop, JSValue v) void js_setprop_str(JSValue obj, const char *prop, JSValue v) { JS_SetPropertyStr(js, obj, prop, v); }
{
JS_SetPropertyStr(js, obj, prop, v);
}
JSValue jstzone() JSValue jstzone()
{ {
@ -140,18 +137,10 @@ int js2int(JSValue v) {
return i; return i;
} }
JSValue int2js(int i) { JSValue int2js(int i) { return JS_NewInt64(js, i); }
return JS_NewInt64(js, i);
}
JSValue str2js(const char *c) { JSValue str2js(const char *c) { return JS_NewString(js, c); }
return JS_NewString(js, c); const char *js2str(JSValue v) { return JS_ToCString(js, v); }
}
const char *js2str(JSValue v)
{
return JS_ToCString(js, v);
}
JSValue strarr2js(const char **c) JSValue strarr2js(const char **c)
{ {
@ -197,7 +186,7 @@ struct glrect js2glrect(JSValue v) {
return rect; return rect;
} }
JSValue js_arridx(JSValue v, int idx) { return js_getpropidx( v, idx); } JSValue js_arridx(JSValue v, int idx) { return js_getpropidx(v, idx); }
int js_arrlen(JSValue v) { int js_arrlen(JSValue v) {
int len; int len;
@ -270,9 +259,23 @@ cpBitmask js2bitmask(JSValue v) {
HMM_Vec2 *js2cpvec2arr(JSValue v) { HMM_Vec2 *js2cpvec2arr(JSValue v) {
HMM_Vec2 *arr = NULL; HMM_Vec2 *arr = NULL;
int n = js_arrlen(v); int n = js_arrlen(v);
for (int i = 0; i < n; i++) arrsetlen(arr,n);
arrput(arr, js2vec2(js_getpropidx( v, i)));
for (int i = 0; i < n; i++)
arr[i] = js2vec2(js_getpropidx( v, i));
return arr;
}
HMM_Vec2 *jsfloat2vec(JSValue v)
{
size_t s;
void *buf = JS_GetArrayBuffer(js, &s, v);
HMM_Vec2 *arr = NULL;
int n = s/2;
n /= sizeof(float);
// arrsetcap(arr,n);
// memcpy(arr,buf,s);
return arr; return arr;
} }
@ -401,6 +404,8 @@ JSValue duk_spline_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst
break; break;
} }
arrfree(points); arrfree(points);
if (!samples) if (!samples)
@ -573,8 +578,8 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break; break;
case 15: case 15:
// music_stop(); gameobject_draw_debug(js2gameobject(argv[1]));
break; break;
case 16: case 16:
// dbg_color = js2color(argv[1]); // dbg_color = js2color(argv[1]);
@ -1054,7 +1059,6 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break; break;
case 128: case 128:
YughWarn("%g",stm_ms(9737310));
ret = JS_NewFloat64(js, stm_ns(js2uint64(argv[1]))); ret = JS_NewFloat64(js, stm_ns(js2uint64(argv[1])));
break; break;
@ -1842,6 +1846,34 @@ JSValue duk_cmd_points(JSContext *js, JSValueConst this, int argc, JSValueConst
return JS_UNDEFINED; return JS_UNDEFINED;
} }
const char *STRTEST = "TEST STRING";
JSValue duk_profile(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
{
int cmd = js2int(argv[0]);
switch(cmd) {
case 0:
break;
case 1:
js2number(argv[1]);
break;
case 2:
js2cpvec2arr(argv[1]);
break;
case 3:
return num2js(1.0);
case 4:
js2str(argv[1]);
break;
case 5:
jsfloat2vec(argv[1]);
break;
case 6:
return JS_NewStringLen(js, STRTEST, sizeof(*STRTEST));
}
return JS_UNDEFINED;
}
#define DUK_FUNC(NAME, ARGS) JS_SetPropertyStr(js, globalThis, #NAME, JS_NewCFunction(js, duk_##NAME, #NAME, ARGS)); #define DUK_FUNC(NAME, ARGS) JS_SetPropertyStr(js, globalThis, #NAME, JS_NewCFunction(js, duk_##NAME, #NAME, ARGS));
void ffi_load() { void ffi_load() {
@ -1880,6 +1912,8 @@ void ffi_load() {
DUK_FUNC(inflate_cpv, 3) DUK_FUNC(inflate_cpv, 3)
DUK_FUNC(profile, 2)
JS_FreeValue(js,globalThis); JS_FreeValue(js,globalThis);
JS_NewClassID(&js_ptr_id); JS_NewClassID(&js_ptr_id);

View file

@ -2,6 +2,8 @@
#define RESOURCES_H #define RESOURCES_H
#include <stdio.h> #include <stdio.h>
#include "stb_ds.h"
#include "string.h"
extern char *DATA_PATH; extern char *DATA_PATH;