spline editing works; hollow, etc; add M-g for move all points

This commit is contained in:
John Alanbrook 2023-10-29 21:39:45 +00:00
parent 0fcc2286fa
commit 6eefa95546
10 changed files with 137 additions and 131 deletions

View file

@ -2,10 +2,11 @@
Script hooks exist to allow to modification of the game.
|config.js|called before any game play|
|config.js|called before any game play, including play from editor|
|game.js|called to start the game|
|editorconfig.js|called when the editor is loaded, used to personalize|
|debug.js|called when play in editor is selected|
|dbgret.js|called when play in editor returns to editor|
All objects in the Yugine can have an associated script. This script can perform setup, teardown, and handles responses for the object.

View file

@ -460,7 +460,6 @@ polygon2d.inputs['C-b'].doc = "Freeze mirroring in place.";
//Object.freeze(polygon2d);
component.edge2d = Object.copy(collider2d, {
degrees:2,
dimensions:2,
thickness:0,
/* open: 0
@ -468,13 +467,8 @@ component.edge2d = Object.copy(collider2d, {
beziers: 2
looped: 3
*/
type: 3,
typeid: {
open: 0,
clamped: 1,
beziers: 2,
looped: 3
},
type: Spline.type.clamped,
looped: false,
flipx: false,
flipy: false,
@ -483,7 +477,7 @@ component.edge2d = Object.copy(collider2d, {
hollow: false,
hollowt: 0,
spoints() {
if (!this.cpoints) return [];
var spoints = this.cpoints.slice();
@ -504,15 +498,12 @@ component.edge2d = Object.copy(collider2d, {
}
}
return spoints;
if (this.hollow) {
var hpoints = [];
var inflatep = inflate_cpv(spoints, spoints.length, this.hollowt);
inflatep[0].slice().reverse().forEach(function(x) { hpoints.push(x); });
inflatep[1].forEach(function(x) { hpoints.push(x); });
return hpoints;
var hpoints = inflate_cpv(spoints, spoints.length, this.hollowt);
if (hpoints.length === spoints.length) return spoints;
var arr1 = hpoints.filter(function(x,i) { return i % 2 === 0; });
var arr2 = hpoints.filter(function(x,i) { return i % 2 !== 0; });
return arr1.concat(arr2.reverse());
}
return spoints;
@ -521,29 +512,29 @@ component.edge2d = Object.copy(collider2d, {
sample(n) {
var spoints = this.spoints();
this.degrees = Math.clamp(this.degrees, 1, spoints.length-1);
var degrees = 2;
if (n < spoints.length) n = spoints.length;
if (spoints.length === 2)
return spoints;
if (spoints.length < 2)
return [];
if (this.degrees < 2) {
if (this.type === 3)
return spoints.wrapped(1);
if (this.samples === spoints.length) {
if (this.looped) return spoints.wrapped(1);
return spoints;
}
/*
order = degrees+1
knots = spoints.length + order
assert knots%order != 0
*/
if (this.type === component.edge2d.typeid.this.looped)
return spline_cmd(0, this.degrees, this.dimensions, 0, spoints.wrapped(this.degrees), n);
if (this.looped)
return Spline.sample(degrees, this.dimensions, Spline.type.open, spoints.wrapped(this.degrees), n);
return spline_cmd(0, this.degrees, this.dimensions, this.type, spoints, n);
return Spline.sample(degrees, this.dimensions, this.type, spoints, n);
},
samples: 10,
@ -580,6 +571,18 @@ component.edge2d = Object.copy(collider2d, {
return undefined;
},
pick_all() {
var picks = [];
this.cpoints.forEach(function(x) {
picks.push({
set pos(n) { x.x = n.x; x.y = n.y; },
get pos() { return x; },
sync: this.sync.bind(this),
});
}, this);
return picks;
},
});
component.edge2d.impl = {
@ -591,6 +594,7 @@ component.edge2d.impl = {
},
get thickness() { return cmd(112,this.id); },
sync() {
if (this.samples < this.spoints().length) this.samples = this.spoints().length;
var sensor = this.sensor;
var points = this.sample(this.samples);
cmd_edge2d(0,this.id,points);
@ -599,64 +603,58 @@ component.edge2d.impl = {
};
var bucket = component.edge2d;
bucket.spoints.doc = "Returns the controls points after modifiers are applied, such as it being hollow or mirrored on its axises.";
bucket.inputs = {};
bucket.inputs.post = function() { this.sync(); };
bucket.inputs.h = function() { this.hollow = !this.hollow; };
bucket.inputs.h.doc = "Toggle hollow.";
bucket.inputs['C-g'] = function() {
this.hollowt--;
if (this.hollowt < 0) this.hollowt = 0;
};
bucket.inputs['C-g'] = function() { if (this.hollowt > 0) this.hollowt--; };
bucket.inputs['C-g'].doc = "Thin the hollow thickness.";
bucket.inputs['C-g'].rep = true;
bucket.inputs['C-f'] = function() { this.hollowt++; };
bucket.inputs['C-f'].doc = "Increase the hollow thickness.";
bucket.inputs['C-f'].rep = true;
bucket.inputs['M-v'] = function() { this.thickness--; };
bucket.inputs['M-v'] = function() { if (this.thickness > 0) this.thickness--; };
bucket.inputs['M-v'].doc = "Decrease spline thickness.";
bucket.inputs['M-v'].rep = true;
bucket.inputs['C-b'] = function() {
bucket.inputs['C-y'] = function() {
this.cpoints = this.spoints();
this.flipx = false;
this.flipy = false;
this.hollow = false;
};
bucket.inputs['C-b'].doc = "Freeze mirroring,";
bucket.inputs['C-y'].doc = "Freeze mirroring,";
bucket.inputs['M-b'] = function() { this.thickness++; };
bucket.inputs['M-b'].doc = "Increase spline thickness.";
bucket.inputs['M-b'].rep = true;
bucket.inputs['C-plus'] = function() { this.degrees++; };
bucket.inputs['C-plus'].doc = "Increase the degrees of this spline.";
bucket.inputs['C-plus'].rep = true;
bucket.inputs.plus = function() { this.samples++; };
bucket.inputs.plus.doc = "Increase the number of samples of this spline.";
bucket.inputs.plus.rep = true;
bucket.inputs.minus = function() {
this.samples--;
if (this.samples < 1) this.samples = 1;
};
bucket.inputs.minus = function() { if (this.samples > this.spoints().length) this.samples--;};
bucket.inputs.minus.doc = "Decrease the number of samples on this spline.";
bucket.inputs.minus.rep = true;
bucket.inputs['C-minus'] = function() { this.degrees--; };
bucket.inputs['C-minus'].doc = "Decrease the number of degrees of this spline.";
bucket.inputs['C-minus'].rep = true;
bucket.inputs['C-r'] = function() { this.cpoints = this.cpoints.reverse(); };
bucket.inputs['C-r'].doc = "Reverse the order of the spline's points.";
bucket.inputs['C-l'] = function() { this.type = 3; };
bucket.inputs['C-l'].doc = "Set type of spline to clamped.";
bucket.inputs['C-l'] = function() { this.looped = !this.looped};
bucket.inputs['C-l'].doc = "Toggle spline being looped.";
bucket.inputs['C-c'] = function() { this.type = 1; };
bucket.inputs['C-c'].doc = "Set type of spline to closed.";
bucket.inputs['C-c'] = function() { this.type = Spline.type.clamped; };
bucket.inputs['C-c'].doc = "Set type of spline to clamped.";
bucket.inputs['C-o'] = function() { this.type = 0; };
bucket.inputs['C-o'] = function() { this.type = Spline.type.open; };
bucket.inputs['C-o'].doc = "Set spline to open.";
bucket.inputs['C-b'] = function() { this.type = Spline.type.bezier; };
bucket.inputs['C-b'].doc = "Set spline to bezier.";
bucket.inputs['C-M-lm'] = function() {
var idx = grab_from_points(Mouse.worldpos, this.cpoints.map(this.gameobject.world2this,this.gameobject), 25);
if (idx === -1) return;

View file

@ -568,6 +568,7 @@ var editor = {
GUI.text(JSON.stringify(o._ed.urdiff,null,1), [500,500]);
*/
},
ed_debug() {
@ -662,6 +663,9 @@ var editor = {
}
editor.inputs = {};
editor.inputs.post = function() {
if (editor.sel_comp && 'sync' in editor.sel_comp) editor.sel_comp.sync();
};
editor.inputs.release_post = function() {
editor.snapshot();
editor.edit_level.check_dirty();
@ -710,7 +714,7 @@ editor.inputs['C-d'].doc = "Duplicate all selected objects.";
editor.inputs['C-m'] = function() {
if (editor.sel_comp) {
if (editor.sel_comp.flipy)
if ('flipy' in editor.sel_comp)
editor.sel_comp.flipy = !editor.sel_comp.flipy;
return;
@ -722,7 +726,7 @@ editor.inputs['C-m'].doc = "Mirror selected objects on the Y axis.";
editor.inputs.m = function() {
if (editor.sel_comp) {
if (editor.sel_comp.flipx)
if ('flipx' in editor.sel_comp)
editor.sel_comp.flipx = !editor.sel_comp.flipx;
return;
@ -1187,17 +1191,22 @@ editor.inputs['M-u'].doc = "Make selected objects unique.";
editor.inputs['C-S-g'] = function() { editor.openpanel(groupsaveaspanel); };
editor.inputs['C-S-g'].doc = "Save selected objects as a new level.";
editor.inputs.g = editor.inputs.mm;/*
editor.inputs.g = function() {
if (editor.selectlist.length === 0) {
var o = editor.try_pick();
if (!o) return;
editor.selectlist = [o];
}
if (editor.sel_comp && 'pick' in editor.sel_comp) {
var o = editor.sel_comp.pick(Mouse.worldpos);
if (o) editor.grabselect = [o];
return;
}
editor.grabselect = editor.selectlist.slice();
editor.grabselect = editor.selectlist.slice();
};
*/
editor.inputs.g.doc = "Move selected objects.";
editor.inputs.g.released = function() { editor.grabselect = []; Mouse.normal(); };
@ -1238,6 +1247,13 @@ editor.inputs['C-g'] = function() {
};
editor.inputs['C-g'].doc = "Duplicate selected objects, then move them.";
editor.inputs['M-g'] = function()
{
if (this.sel_comp && 'pick_all' in this.sel_comp)
this.grabselect = this.sel_comp.pick_all();
}
editor.inputs['M-g'].doc = "Move all.";
editor.inputs['C-lb'] = function() {
editor_config.grid_size -= Keys.shift() ? 10 : 1;
if (editor_config.grid_size <= 0) editor_config.grid_size = 1;
@ -1989,6 +2005,7 @@ limited_editor.inputs['C-q'] = function()
{
Sound.killall();
Primum.clear();
load("dbgret.js");
editor.enter_editor();
}

View file

@ -595,6 +595,18 @@ function Color(from) {
};
*/
var Spline = {};
Spline.sample = function(degrees, dimensions, type, ctrl_points, nsamples)
{
var s = spline_cmd(0, degrees,dimensions,type,ctrl_points,nsamples);
return s;
}
Spline.type = {
open: 0,
clamped: 1,
beziers: 2
};
load("scripts/components.js");
function find_com(objects)
@ -853,3 +865,4 @@ Game.view_camera = function(cam)
Window.name = "Primum Machinam (V0.1)";
Window.width = 1280;
Window.height = 720;

View file

@ -414,7 +414,7 @@ var gameobject = {
this.level = undefined;
}
Player.uncontrol(this);
Player.do_uncontrol(this);
Register.unregister_obj(this);
// ur[this.ur].instances.remove(this);
this.body = -1;

View file

@ -165,7 +165,7 @@ var Player = {
}
},
uncontrol(pawn) {
do_uncontrol(pawn) {
this.players.forEach(function(p) {
p.pawns = p.pawns.filter(x => x !== pawn);
});

View file

@ -370,51 +370,45 @@ float vecs2m(cpVect a, cpVect b)
return (b.y-a.y)/(b.x-a.x);
}
cpVect inflatepoint(cpVect a, cpVect b, cpVect c, float d)
{
cpVect ba = cpvnormalize(cpvsub(a,b));
cpVect bc = cpvnormalize(cpvsub(c,b));
cpVect avg = cpvadd(ba, bc);
avg = cpvmult(avg, 0.5);
float dot = cpvdot(ba, bc);
dot /= cpvlength(ba);
dot /= cpvlength(bc);
float mid = acos(dot)/2;
avg = cpvnormalize(avg);
return cpvadd(b, cpvmult(avg, d/sin(mid)));
}
void inflatepoints(cpVect *r, cpVect *p, float d, int n)
cpVect *inflatepoints(cpVect *p, float d, int n)
{
if (d == 0) {
cpVect *ret = NULL;
arraddn(ret,n);
for (int i = 0; i < n; i++)
r[i] = p[i];
ret[i] = p[i];
return;
return ret;
}
if (cpveql(p[0], p[n-1])) {
r[0] = inflatepoint(p[n-2],p[0],p[1],d);
r[n-1] = r[0];
} else {
cpVect outdir = cpvmult(cpvnormalize(cpvsub(p[0],p[1])),fabs(d));
cpVect perp;
if (d > 0)
perp = cpvperp(outdir);
else
perp = cpvrperp(outdir);
r[0] = cpvadd(p[0],cpvadd(outdir,perp));
outdir = cpvmult(cpvnormalize(cpvsub(p[n-1],p[n-2])),fabs(d));
if (d > 0)
perp = cpvrperp(outdir);
else
perp = cpvperp(outdir);
r[n-1] = cpvadd(p[n-1],cpvadd(outdir,perp));
}
parsl_position par_v[n];
uint16_t spine_lens[] = {n};
for (int i = 0; i < n; i++) {
par_v[i].x = p[i].x;
par_v[i].y = p[i].y;
};
for (int i = 0; i < n-2; i++)
r[i+1] = inflatepoint(p[i],p[i+1],p[i+2], d);
parsl_context *par_ctx = parsl_create_context((parsl_config){
.thickness = d,
.flags= PARSL_FLAG_ANNOTATIONS,
.u_mode = PAR_U_MODE_DISTANCE
});
parsl_mesh *mesh = parsl_mesh_from_lines(par_ctx, (parsl_spine_list){
.num_vertices = n,
.num_spines = 1,
.vertices = par_v,
.spine_lengths = spine_lens,
.closed = 0,
});
cpVect *ret = NULL;
arraddn(ret,mesh->num_vertices);
for (int i = 0; i < mesh->num_vertices; i++) {
ret[i].x = mesh->positions[i].x;
ret[i].y = mesh->positions[i].y;
};
return ret;
}
void draw_edge(cpVect *points, int n, struct rgba color, int thickness, int closed, int flags, struct rgba line_color, float line_seg)
@ -443,7 +437,6 @@ void draw_edge(cpVect *points, int n, struct rgba color, int thickness, int clos
parsl_context *par_ctx = parsl_create_context((parsl_config){
.thickness = thickness,
.flags = PARSL_FLAG_ANNOTATIONS,
.u_mode = PAR_U_MODE_DISTANCE,
});
parsl_mesh *mesh = parsl_mesh_from_lines(par_ctx, (parsl_spine_list){

View file

@ -24,7 +24,6 @@ void debug_flush(HMM_Mat4 *view);
void debug_newframe();
void debug_nextpass();
cpVect inflatepoint(cpVect a, cpVect b, cpVect c, float d);
void inflatepoints(cpVect *r, cpVect *p, float d, int n);
cpVect *inflatepoints(cpVect *p, float d, int n);
#endif

View file

@ -237,7 +237,7 @@ cpBitmask js2bitmask(JSValue v) {
cpVect *cpvecarr = NULL;
/* Must be freed */
/* Does not need to be freed by returning; but not reentrant */
cpVect *js2cpvec2arr(JSValue v) {
if (cpvecarr)
arrfree(cpvecarr);
@ -362,30 +362,24 @@ JSValue duk_spline_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst
tsBSpline spline;
int d = js2int(argv[2]); /* dimensions */
int degrees = js2int(argv[1]);
int d = js2int(argv[2]); /* dimensions */
int type = js2int(argv[3]);
JSValue ctrl_pts = argv[4];
int n = js_arrlen(ctrl_pts);
cpVect *points = js2cpvec2arr(argv[4]);
size_t nsamples = js2int(argv[5]);
cpVect points[n];
tsStatus status;
ts_bspline_new(n, d, degrees, type, &spline, &status);
ts_bspline_new(arrlen(points), d, degrees, type, &spline, &status);
if (status.code)
YughCritical("Spline creation error %d: %s", status.code, status.message);
for (int i = 0; i < n; i++)
points[i] = js2vec2(js_getpropidx( ctrl_pts, i));
ts_bspline_set_control_points(&spline, (tsReal *)points, &status);
ts_bspline_set_control_points(&spline, (tsReal*)points, &status);
if (status.code)
YughCritical("Spline creation error %d: %s", status.code, status.message);
cpVect samples[nsamples];
cpVect *samples = malloc(nsamples*sizeof(cpVect));
size_t rsamples;
/* TODO: This does not work with Clang/GCC due to UB */
@ -394,16 +388,10 @@ JSValue duk_spline_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst
if (status.code)
YughCritical("Spline creation error %d: %s", status.code, status.message);
JSValue arr = JS_NewArray(js);
for (int i = 0; i < nsamples; i++) {
JSValue psample = JS_NewArray(js);
js_setprop_num(psample, 0, float2js(samples[i].x));
js_setprop_num(psample, 1, float2js(samples[i].y));
js_setprop_num(arr, i, psample);
}
JSValue arr = vecarr2js(samples, nsamples);
ts_bspline_free(&spline);
free(samples);
return arr;
}
@ -1587,16 +1575,9 @@ JSValue duk_inflate_cpv(JSContext *js, JSValueConst this, int argc, JSValueConst
cpVect *points = js2cpvec2arr(argv[0]);
int n = js2int(argv[1]);
double d = js2number(argv[2]);
cpVect inflate_out[n];
cpVect inflate_in[n];
inflatepoints(inflate_out, points, d, n);
inflatepoints(inflate_in, points, -d, n);
JSValue arr = JS_NewArray(js);
js_setprop_num(arr, 0, vecarr2js(inflate_out, n));
js_setprop_num(arr, 1, vecarr2js(inflate_in, n));
cpVect *infl = inflatepoints(points,d,n);
JSValue arr = vecarr2js(infl,arrlen(infl));
arrfree(infl);
return arr;
}

View file

@ -263,6 +263,10 @@ const char *keyname_extd(int key) {
return "space";
case SAPP_KEYCODE_KP_ADD:
return "plus";
case '=':
return "plus";
case '-':
return "minus";
case SAPP_KEYCODE_KP_SUBTRACT:
return "minus";
case SAPP_KEYCODE_GRAVE_ACCENT: