Fix scaling and rotating controls

This commit is contained in:
John Alanbrook 2024-04-09 08:01:54 -05:00
parent 53f2addeec
commit 5051d11005
3 changed files with 62 additions and 79 deletions

View file

@ -447,6 +447,7 @@ var editor = {
depth++; depth++;
render.text("$$$$$$", [0,ypos],1,editor.color_depths[depth]); render.text("$$$$$$", [0,ypos],1,editor.color_depths[depth]);
this.selectlist.forEach(function(x) { this.selectlist.forEach(function(x) {
render.text(x.urstr(), x.screenpos().add([0, 32]), 1, Color.editor.ur); render.text(x.urstr(), x.screenpos().add([0, 32]), 1, Color.editor.ur);
render.text(x.pos.map(function(x) { return Math.round(x); }), x.screenpos(), 1, Color.white); render.text(x.pos.map(function(x) { return Math.round(x); }), x.screenpos(), 1, Color.white);
@ -468,7 +469,7 @@ var editor = {
} }
if (this.rotlist.length === 1) if (this.rotlist.length === 1)
render.text(Math.trunc(this.rotlist[0].obj.angle), input.mouse.screenpos(), 1, Color.teal); render.text(Math.places(this.rotlist[0].angle, 3), input.mouse.screenpos(), 1, Color.teal);
if (this.selectlist.length === 1) { if (this.selectlist.length === 1) {
var i = 1; var i = 1;
@ -639,7 +640,7 @@ editor.inputs['C-b'] = function() {
c.grow(obj.scale); c.grow(obj.scale);
c.sync?.(); c.sync?.();
}); });
obj.scale = [1,1,1]; obj.set_scale([1,1,1]);
} }
editor.inputs.drop = function(str) { editor.inputs.drop = function(str) {
@ -763,7 +764,7 @@ editor.inputs.m = function() {
return; return;
} }
editor.selectlist.forEach(obj => obj.scale = [-obj.scale[0], obj.scale[1]]); editor.selectlist.forEach(obj => obj.grow([-1,1,1]));
}; };
editor.inputs.m.doc = "Mirror selected objects on the X axis."; editor.inputs.m.doc = "Mirror selected objects on the X axis.";
@ -798,29 +799,16 @@ editor.inputs['C-F'] = function() {
}; };
editor.inputs['C-F'].doc = "Tunnel out of the level you are editing, saving it in the process."; editor.inputs['C-F'].doc = "Tunnel out of the level you are editing, saving it in the process.";
editor.inputs['C-r'] = function() { editor.selectlist.forEach(function(x) { x.angle = -x.angle; }); }; editor.inputs['C-r'] = function() { editor.selectlist.forEach(function(x) { x.rotate(-x.angle*2); }); }
editor.inputs['C-r'].doc = "Negate the selected's angle."; editor.inputs['C-r'].doc = "Negate the selected's angle.";
editor.inputs.r = function() { editor.inputs.r = function() {
if (editor.sel_comp && 'angle' in editor.sel_comp) { if (editor.sel_comp && 'angle' in editor.sel_comp) {
var relpos = input.mouse.worldpos().sub(editor.sel_comp.gameobject.pos); var relpos = input.mouse.worldpos().sub(editor.sel_comp.gameobject.pos);
editor.startoffset = Math.atan2(relpos.y, relpos.x);
editor.startrot = editor.sel_comp.angle;
return; return;
} }
editor.rotlist = []; editor.rotlist = editor.selectlist;
editor.selectlist.forEach(function(x) {
var relpos = input.mouse.worldpos().sub(editor.cursor);
editor.rotlist.push({
obj: x,
angle: x.angle,
pos: x.pos,
offset: x.pos.sub(editor.cursor),
rotoffset: Math.atan2(relpos.y, relpos.x),
});
});
}; };
editor.inputs.r.doc = "Rotate selected using the mouse while held down."; editor.inputs.r.doc = "Rotate selected using the mouse while held down.";
editor.inputs.r.released = function() { editor.rotlist = []; } editor.inputs.r.released = function() { editor.rotlist = []; }
@ -1160,32 +1148,25 @@ editor.inputs.mouse.move = function(pos, dpos)
} }
editor.grabselect?.forEach(function(x) { editor.grabselect?.forEach(function(x) {
if (!x) return;
x.move(game.camera.dir_view2world(dpos)); x.move(game.camera.dir_view2world(dpos));
x.sync(); x.sync();
}); });
var relpos = input.mouse.worldpos().sub(editor.cursor); var relpos = input.mouse.worldpos().sub(editor.cursor);
var dist = Vector.length(relpos); var lastpos = relpos.sub(dpos);
editor.scalelist?.forEach(function(x) { var dist = Vector.length(relpos.add(dpos)) - Vector.length(relpos);
var scalediff = dist / x.scaleoffset; var scalediff = 1+(dist/editor.scaleoffset);
if (typeof x.obj.scale === 'number')
x.obj.scale = x.scale * scalediff;
else {
x.obj.scale = x.scale.map(x=> x * scalediff);
if (x.offset)
x.obj.pos = editor.cursor.add(x.offset.scale(scalediff));
}
});
editor.scalelist?.forEach(function(x) { x.grow(scalediff); });
var anglediff = Math.atan2(relpos.y, relpos.x) - Math.atan2(lastpos.y, lastpos.x);
editor.rotlist?.forEach(function(x) { editor.rotlist?.forEach(function(x) {
var anglediff = Math.atan2(relpos.y, relpos.x) - x.rotoffset; x.rotate(anglediff/(2*Math.PI));
x.obj.angle = x.angle + Math.rad2turn(anglediff); if (input.keyboard.down('shift')) {
if (input.keyboard.down('shift')) var rotate = Math.nearest(x.angle, 1/24) - x.angle;
x.obj.angle = Math.nearest(x.obj.angle, (1/24)); x.rotate(rotate)
if (x.pos) }
x.obj.pos = x.pos.sub(x.offset).add(x.offset.rotate(anglediff));
}); });
} }
@ -1211,11 +1192,7 @@ editor.inputs.delete.doc = "Delete selected objects.";
editor.inputs['S-d'] = editor.inputs.delete; editor.inputs['S-d'] = editor.inputs.delete;
editor.inputs['C-k'] = editor.inputs.delete; editor.inputs['C-k'] = editor.inputs.delete;
editor.inputs['C-u'] = function() { editor.inputs['C-u'] = function() { this.selectlist.forEach(x => x.revert()); };
this.selectlist.forEach(function(x) {
x.revert();
});
};
editor.inputs['C-u'].doc = "Revert selected objects back to their prefab form."; editor.inputs['C-u'].doc = "Revert selected objects back to their prefab form.";
editor.inputs['M-u'] = function() { editor.inputs['M-u'] = function() {
@ -1381,27 +1358,16 @@ compmode.inputs['C-x'] = function() {};
editor.scalelist = []; editor.scalelist = [];
editor.inputs.s = function() { editor.inputs.s = function() {
var scaleoffset = Vector.length(input.mouse.worldpos().sub(editor.cursor)); editor.scaleoffset = Vector.length(input.mouse.worldpos().sub(editor.cursor));
editor.scalelist = []; editor.scalelist = [];
if (editor.sel_comp) { if (editor.sel_comp) {
if (!('scale' in editor.sel_comp)) return; if (!('scale' in editor.sel_comp)) return;
editor.scalelist.push({ editor.scalelist.push(editor.sel_comp);
obj: editor.sel_comp,
scale: editor.sel_comp.scale,
scaleoffset: scaleoffset,
});
return; return;
} }
editor.selectlist.forEach(function(x) { editor.scalelist = editor.selectlist;
editor.scalelist.push({
obj: x,
scale: x.scale,
offset: x.pos.sub(editor.cursor),
scaleoffset: scaleoffset,
});
});
}; };
editor.inputs.s.doc = "Scale selected."; editor.inputs.s.doc = "Scale selected.";

View file

@ -144,28 +144,38 @@ var gameobject = {
return audio.cry(file); return audio.cry(file);
}, },
set pos(x) { this.set_pos(x); },
get pos() { return this.rpos; },
set angle(x) { this.set_angle(x); },
get angle() { return this.rangle; },
set scale(x) { this.set_scale(x); },
get scale() { return this.rscale; },
set_pos(x, relative = world) { set_pos(x, relative = world) {
var move = x.sub(this.pos); var newpos = x.add(relative.pos);
this.pos = x; var move = newpos.sub(this.pos);
this.rpos = newpos;
this.objects.forEach(x => x.move(move)); this.objects.forEach(x => x.move(move));
}, },
set_angle(x, relative = world) { set_angle(x, relative = world) {
var diff = x - this.angle; var newangle = relative.angle + x;
this.angle = x; var diff = newangle - this.angle;
this.rangle = newangle;
this.objects.forEach(obj => { this.objects.forEach(obj => {
obj.rotate(diff); obj.rotate(diff);
obj.set_pos(Vector.rotate(obj.pos, diff)); obj.set_pos(Vector.rotate(obj.get_pos(obj.master), diff), obj.master);
}); });
}, },
set_scale(x, relative = world) { set_scale(x, relative = world) {
if (typeof x === 'number') x = [x,x,x]; if (typeof x === 'number') x = [x,x,x];
var pct = this.scale.map((s,i) => x[i]/s); var newscale = relative.scale.map((s,i) => x[i]*s);
this.scale = x; var pct = this.scale.map((s,i) => newscale[i]/s);
this.rscale = newscale;
this.objects.forEach(obj => { this.objects.forEach(obj => {
obj.grow(pct); obj.grow(pct);
obj.set_pos(obj.pos.map((x,i) => x*pct[i])); obj.set_pos(obj.get_pos(obj.master).map((x,i) => x*pct[i]), obj.master);
}); });
}, },
@ -176,19 +186,22 @@ var gameobject = {
get_angle(relative = world) { get_angle(relative = world) {
if (relative === world) return this.angle; if (relative === world) return this.angle;
return this.master.angle - this.angle; return this.angle - relative.angle;
}, },
get_scale(relative = world) { get_scale(relative = world) {
if (relative === world) return this.scale; if (relative === world) return this.scale;
var masterscale = this.master.scale; var masterscale = relative.scale;
return this.scale.map((x,i) => x/masterscale[i]); return this.scale.map((x,i) => x/masterscale[i]);
}, },
/* Moving, rotating, scaling functions, world relative */ /* Moving, rotating, scaling functions, world relative */
move(vec) { this.set_pos(this.pos.add(vec)); }, move(vec) { this.set_pos(this.pos.add(vec)); },
rotate(x) { this.set_angle(this.angle + x); }, rotate(x) { this.set_angle(this.angle + x); },
grow(vec) { this.set_scale(this.scale.map((x, i) => x * vec[i])); }, grow(vec) {
if (typeof vec === 'number') vec = [vec,vec,vec];
this.set_scale(this.scale.map((x, i) => x * vec[i]));
},
screenpos() { return game.camera.world2view(this.pos); }, screenpos() { return game.camera.world2view(this.pos); },
@ -277,6 +290,9 @@ var gameobject = {
if (callback) callback(ent); if (callback) callback(ent);
ent.ur.fresh ??= json.decode(json.encode(ent)); ent.ur.fresh ??= json.decode(json.encode(ent));
ent.ur.fresh.objects = {};
for (var i in ent.objects)
ent.ur.fresh.objects[i] = ent.objects[i].instance_obj();
return ent; return ent;
}, },
@ -430,11 +446,12 @@ var gameobject = {
transform() { transform() {
var t = {}; var t = {};
t.pos = this.pos; t.pos = this.get_pos(this.master);
if (t.pos.every(x => x === 0)) delete t.pos; if (t.pos.every(x => x === 0)) delete t.pos;
t.angle = Math.places(this.angle, 4); t.angle = Math.places(this.get_angle(this.master), 4);
if (t.angle === 0) delete t.angle; if (t.angle === 0) delete t.angle;
t.scale = this.scale; return t;
t.scale = this.get_scale(this.master);
t.scale = t.scale.map((x, i) => x / this.ur.fresh.scale[i]); t.scale = t.scale.map((x, i) => x / this.ur.fresh.scale[i]);
t.scale = t.scale.map(x => Math.places(x, 3)); t.scale = t.scale.map(x => Math.places(x, 3));
if (t.scale.every(x => x === 1)) delete t.scale; if (t.scale.every(x => x === 1)) delete t.scale;

View file

@ -1119,16 +1119,18 @@ static const JSCFunctionListEntry js_window_funcs[] = {
MIST_FUNC_DEF(window, set_icon, 1) MIST_FUNC_DEF(window, set_icon, 1)
}; };
JSValue js_gameobject_set_pos(JSContext *js, JSValue this, JSValue val) { JSValue js_gameobject_set_rpos(JSContext *js, JSValue this, JSValue val) {
cpBody *b = js2gameobject(this)->body; cpBody *b = js2gameobject(this)->body;
cpBodySetPosition(b, js2cvec2(val)); cpBodySetPosition(b, js2cvec2(val));
if (cpBodyGetType(b) == CP_BODY_TYPE_STATIC) if (cpBodyGetType(b) == CP_BODY_TYPE_STATIC)
cpSpaceReindexShapesForBody(space, b); cpSpaceReindexShapesForBody(space, b);
return JS_UNDEFINED; return JS_UNDEFINED;
} }
JSValue js_gameobject_get_pos(JSContext *js, JSValue this) { return cvec22js(cpBodyGetPosition(js2gameobject(this)->body)); } JSValue js_gameobject_get_rpos(JSContext *js, JSValue this) { return cvec22js(cpBodyGetPosition(js2gameobject(this)->body)); }
JSValue js_gameobject_set_angle (JSContext *js, JSValue this, JSValue val) { cpBodySetAngle(js2gameobject(this)->body, HMM_TurnToRad*js2number(val)); } JSValue js_gameobject_set_rangle (JSContext *js, JSValue this, JSValue val) { cpBodySetAngle(js2gameobject(this)->body, HMM_TurnToRad*js2number(val)); }
JSValue js_gameobject_get_angle (JSContext *js, JSValue this) { return number2js(HMM_RadToTurn*cpBodyGetAngle(js2gameobject(this)->body)); } JSValue js_gameobject_get_rangle (JSContext *js, JSValue this) { return number2js(HMM_RadToTurn*cpBodyGetAngle(js2gameobject(this)->body)); }
JSValue js_gameobject_get_rscale(JSContext *js, JSValue this) { return vec32js(js2gameobject(this)->scale); }
JSValue js_gameobject_set_rscale(JSContext *js, JSValue this, JSValue val) { js2gameobject(this)->scale = js2vec3(val); }
JSC_GETSET_BODY(velocity, Velocity, cvec2) JSC_GETSET_BODY(velocity, Velocity, cvec2)
JSValue js_gameobject_set_angularvelocity (JSContext *js, JSValue this, JSValue val) { cpBodySetAngularVelocity(js2gameobject(this)->body, HMM_TurnToRad*js2number(val)); } JSValue js_gameobject_set_angularvelocity (JSContext *js, JSValue this, JSValue val) { cpBodySetAngularVelocity(js2gameobject(this)->body, HMM_TurnToRad*js2number(val)); }
JSValue js_gameobject_get_angularvelocity (JSContext *js, JSValue this) { return number2js(HMM_RadToTurn*cpBodyGetAngularVelocity(js2gameobject(this)->body)); } JSValue js_gameobject_get_angularvelocity (JSContext *js, JSValue this) { return number2js(HMM_RadToTurn*cpBodyGetAngularVelocity(js2gameobject(this)->body)); }
@ -1142,7 +1144,6 @@ JSC_GETSET_APPLY(gameobject, elasticity, number)
JSC_GETSET_APPLY(gameobject, mass, number) JSC_GETSET_APPLY(gameobject, mass, number)
JSC_GETSET_APPLY(gameobject, phys, number) JSC_GETSET_APPLY(gameobject, phys, number)
JSC_GETSET_APPLY(gameobject, layer, number) JSC_GETSET_APPLY(gameobject, layer, number)
JSC_GETSET(gameobject, scale, vec3)
JSC_GETSET(gameobject, damping, number) JSC_GETSET(gameobject, damping, number)
JSC_GETSET(gameobject, timescale, number) JSC_GETSET(gameobject, timescale, number)
JSC_GETSET(gameobject, maxvelocity, number) JSC_GETSET(gameobject, maxvelocity, number)
@ -1160,16 +1161,15 @@ static const JSCFunctionListEntry js_gameobject_funcs[] = {
CGETSET_ADD(gameobject, elasticity), CGETSET_ADD(gameobject, elasticity),
CGETSET_ADD(gameobject,mass), CGETSET_ADD(gameobject,mass),
CGETSET_ADD(gameobject,damping), CGETSET_ADD(gameobject,damping),
CGETSET_ADD(gameobject, scale),
CGETSET_ADD(gameobject,timescale), CGETSET_ADD(gameobject,timescale),
CGETSET_ADD(gameobject,maxvelocity), CGETSET_ADD(gameobject,maxvelocity),
CGETSET_ADD(gameobject,maxangularvelocity), CGETSET_ADD(gameobject,maxangularvelocity),
CGETSET_ADD(gameobject,layer), CGETSET_ADD(gameobject,layer),
CGETSET_ADD(gameobject,warp_filter), CGETSET_ADD(gameobject,warp_filter),
CGETSET_ADD(gameobject,scale),
CGETSET_ADD(gameobject,drawlayer), CGETSET_ADD(gameobject,drawlayer),
CGETSET_ADD(gameobject, pos), CGETSET_ADD(gameobject, rpos),
CGETSET_ADD(gameobject, angle), CGETSET_ADD(gameobject, rangle),
CGETSET_ADD(gameobject, rscale),
CGETSET_ADD(gameobject, velocity), CGETSET_ADD(gameobject, velocity),
CGETSET_ADD(gameobject, angularvelocity), CGETSET_ADD(gameobject, angularvelocity),
CGETSET_ADD(gameobject, moi), CGETSET_ADD(gameobject, moi),