flip replaced with mirroring'

This commit is contained in:
John Alanbrook 2023-11-15 22:42:39 +00:00
parent f6161d04b0
commit 563bacdb9c
9 changed files with 134 additions and 82 deletions

View file

@ -296,6 +296,16 @@ Object.defineProperty(Object.prototype, 'forEach', {
} }
}); });
Object.defineProperty(Object.prototype, 'map', {
value: function(fn) {
var a = [];
Object.values(this).forEach(function(x) {
a.push(fn(x));
});
return a;
}
});
Object.defineProperty(Object.prototype, 'empty', { Object.defineProperty(Object.prototype, 'empty', {
get: function() { get: function() {
return Object.keys(this).empty; return Object.keys(this).empty;
@ -1082,7 +1092,7 @@ var Vector = {
}, },
dot(a, b) { dot(a, b) {
return cmd(88,a,b);
}, },
random() { random() {
@ -1115,6 +1125,11 @@ var Vector = {
return eql; return eql;
}, },
reflect(vec, plane) {
var p = Vector.norm(plane);
return vec.sub(p.scale(2*Vector.dot(vec, p)));
},
}; };

View file

@ -11,7 +11,6 @@ var editor_config = {
}; };
var configs = { var configs = {
toString() { return "configs"; }, toString() { return "configs"; },
editor: editor_config, editor: editor_config,
@ -740,6 +739,7 @@ editor.inputs['C-d'] = function() {
}; };
editor.inputs['C-d'].doc = "Duplicate all selected objects."; editor.inputs['C-d'].doc = "Duplicate all selected objects.";
editor.inputs['C-m'] = function() { editor.inputs['C-m'] = function() {
if (editor.sel_comp) { if (editor.sel_comp) {
if ('flipy' in editor.sel_comp) if ('flipy' in editor.sel_comp)
@ -748,7 +748,7 @@ editor.inputs['C-m'] = function() {
return; return;
} }
editor.selectlist.forEach(function(x) { x.flipy = !x.flipy; }); editor.selectlist.forEach(function(x) { x.mirror([0,1]);});
}; };
editor.inputs['C-m'].doc = "Mirror selected objects on the Y axis."; editor.inputs['C-m'].doc = "Mirror selected objects on the Y axis.";
@ -760,10 +760,11 @@ editor.inputs.m = function() {
return; return;
} }
editor.selectlist.forEach(function(x) { x.flipx = !x.flipx; }); editor.selectlist.forEach(x => x.mirror([1,0]));
}; };
editor.inputs.m.doc = "Mirror selected objects on the X axis."; editor.inputs.m.doc = "Mirror selected objects on the X axis.";
editor.inputs.q = function() { editor.comp_info = !editor.comp_info; }; editor.inputs.q = function() { editor.comp_info = !editor.comp_info; };
editor.inputs.q.doc = "Toggle help for the selected component."; editor.inputs.q.doc = "Toggle help for the selected component.";
@ -1166,7 +1167,7 @@ editor.inputs.mouse.move = function(pos, dpos)
editor.scalelist?.forEach(function(x) { editor.scalelist?.forEach(function(x) {
var scalediff = dist / x.scaleoffset; var scalediff = dist / x.scaleoffset;
x.obj.scale = x.scale * scalediff; x.obj.scale = x.scale.map(x=> x * scalediff);
if (x.offset) if (x.offset)
x.obj.pos = editor.cursor.add(x.offset.scale(scalediff)); x.obj.pos = editor.cursor.add(x.offset.scale(scalediff));
}); });

View file

@ -819,8 +819,8 @@ var preprimum = globalThis.preprimum;
preprimum.objects = {}; preprimum.objects = {};
preprimum.worldpos = function() { return [0,0]; }; preprimum.worldpos = function() { return [0,0]; };
preprimum.worldangle = function() { return 0; }; preprimum.worldangle = function() { return 0; };
preprimum.scale = 1; preprimum.scale = [1,1,1];
preprimum.gscale = function() { return 1; }; preprimum.gscale = function() { return [1,1,1]; };
preprimum.pos = [0,0]; preprimum.pos = [0,0];
preprimum.angle = 0; preprimum.angle = 0;
preprimum.remove_obj = function() {}; preprimum.remove_obj = function() {};

View file

@ -57,39 +57,38 @@ var gameobject = {
gscale() { return cmd(103,this.body); }, gscale() { return cmd(103,this.body); },
sgscale(x) { sgscale(x) {
if (typeof x === 'number') if (typeof x === 'number')
cmd(36,this.body, [x,x]); x = [x,x];
else cmd(36,this.body,x)
cmd(36,this.body,x)
}, },
get scale() { get scale() {
if (!this.level) return this.gscale(); if (!this.level) return this.gscale();
return this.gscale()/this.level.gscale(); return this.gscale().map((x,i) => x/this.level.gscale()[i]);
}, },
set scale(x) { set scale(x) {
if (this.level) if (typeof x === 'number')
x *= this.level.gscale(); x = [x,x];
var pct = x/this.gscale();
if (this.level) {
var g = this.level.gscale();
x = x.map((y,i) => y * g[i]);
}
var pct = x.map(function(y,i) { return y/this.gscale()[i]; }, this);
this.sgscale(x); this.sgscale(x);
// cmd(36, this.body, x); /* TRANSLATE ALL SUB OBJECTS */
this.objects?.forEach(function(obj) {
obj.sgscale(obj.gscale()*pct);
obj.pos = obj.pos.scale(pct);
});
}, },
set pos(x) { set pos(x) {
this.set_worldpos(Vector.rotate(x.scale(this.level.gscale()),Math.deg2rad(this.level.worldangle())).add(this.level.worldpos())); if (!x[0] || !x[1]) return;
this.set_worldpos(this.level.this2world(x));
}, },
get pos() { get pos() {
if (!this.level) return this.worldpos(); if (!this.level) return this.worldpos();
var offset = this.worldpos().sub(this.level.worldpos()); return this.level.world2this(this.worldpos());
offset = Vector.rotate(offset, -Math.deg2rad(this.level.angle));
offset = offset.scale(1/this.level.gscale());
return offset;
}, },
get elasticity() { return cmd(107,this.body); }, get elasticity() { return cmd(107,this.body); },
set elasticity(x) { cmd(106,this.body,x); }, set elasticity(x) { cmd(106,this.body,x); },
@ -118,9 +117,9 @@ var gameobject = {
set angularvelocity(x) { set_body(8, this.body, Math.deg2rad(x)); }, set angularvelocity(x) { set_body(8, this.body, Math.deg2rad(x)); },
worldpos() { return q_body(1,this.body); }, worldpos() { return q_body(1,this.body); },
set_worldpos(x) { set_worldpos(x) {
var diff = x.sub(this.worldpos()); var poses = this.objects.map(x => x.pos);
this.objects.forEach(function(x) { x.set_worldpos(x.worldpos().add(diff)); });
set_body(2,this.body,x); set_body(2,this.body,x);
this.objects.forEach((o,i) => o.set_worldpos(this.this2world(poses[i])));
}, },
worldangle() { return Math.rad2deg(q_body(2,this.body))%360; }, worldangle() { return Math.rad2deg(q_body(2,this.body))%360; },
@ -149,7 +148,6 @@ var gameobject = {
rotate(x) { rotate(x) {
this.sworldangle(this.worldangle()+x); this.sworldangle(this.worldangle()+x);
}, },
spawn_from_instance(inst) { spawn_from_instance(inst) {
return this.spawn(inst.ur, inst); return this.spawn(inst.ur, inst);
@ -301,9 +299,12 @@ var gameobject = {
velocity:[0,0], velocity:[0,0],
angularvelocity:0, angularvelocity:0,
phys:Physics.static, phys:Physics.static,
flipx:false, flipx() { return this.scale.x < 0; },
flipy:false, flipy() { return this.scale.y < 0; },
scale:1, scale:[1,1,1],
mirror(plane) {
this.scale = Vector.reflect(this.scale, plane);
},
elasticity:0.5, elasticity:0.5,
friction:1, friction:1,
gravity: true, gravity: true,
@ -618,8 +619,8 @@ gameobject.doc = {
velocity: "Velocity of the object, relative to world.", velocity: "Velocity of the object, relative to world.",
angularvelocity: "Angular velocity of the object, relative to the world.", angularvelocity: "Angular velocity of the object, relative to the world.",
scale: "Scale of the object, relative to its level.", scale: "Scale of the object, relative to its level.",
flipx: "Set the object to be flipped on its x axis.", flipx: "Check if the object is flipped on its x axis.",
flipy: "Set the object to be flipped on its y axis.", flipy: "Check if the object is flipped on its y axis.",
elasticity: `When two objects collide, their elasticities are multiplied together. Their velocities are then multiplied by this value to find their resultant velocities.`, elasticity: `When two objects collide, their elasticities are multiplied together. Their velocities are then multiplied by this value to find their resultant velocities.`,
friction: `When one object touches another, friction slows them down.`, friction: `When one object touches another, friction slows them down.`,
gravity: 'True if this object should be affected by gravity.', gravity: 'True if this object should be affected by gravity.',

View file

@ -220,15 +220,9 @@ void phys2d_circledel(struct phys2d_circle *c) {
phys2d_shape_del(&c->shape); phys2d_shape_del(&c->shape);
} }
HMM_Vec2 bodytransformpoint(cpBody *body, cpVect offset) {
HMM_Vec2 pos;
pos.cp = offset;
struct gameobject *go = id2go(body2id(body));
return go2world(go, pos);
}
void phys2d_dbgdrawcpcirc(cpCircleShape *c) { void phys2d_dbgdrawcpcirc(cpCircleShape *c) {
HMM_Vec2 pos = bodytransformpoint(cpShapeGetBody(c), cpCircleShapeGetOffset(c)); HMM_Mat3 rt = mt_rt(go2t(shape2go(c)));
HMM_Vec2 pos = mat_t_pos(rt, (HMM_Vec2)cpCircleShapeGetOffset(c));
float radius = cpCircleShapeGetRadius(c); float radius = cpCircleShapeGetRadius(c);
struct rgba color = shape_color(c); struct rgba color = shape_color(c);
float seglen = cpShapeGetSensor(c) ? 5 : -1; float seglen = cpShapeGetSensor(c) ? 5 : -1;
@ -272,12 +266,12 @@ float phys2d_box_moi(struct phys2d_box *box, float m) {
return cpMomentForBox(m, box->w, box->h); return cpMomentForBox(m, box->w, box->h);
} }
cpTransform trs2cpt(HMM_Vec2 t, float angle, HMM_Vec2 s) { cpTransform trs2cpt(HMM_Vec2 t, float r, HMM_Vec2 s) {
cpTransform T; cpTransform T;
T.a = cos(angle) * s.X; T.a = cos(r) * s.X;
T.b = -sin(angle) * s.X; T.b = -sin(r) * s.X;
T.c = sin(angle) * s.Y; T.c = sin(r) * s.Y;
T.d = cos(angle) * s.Y; T.d = cos(r) * s.Y;
T.tx = t.X * s.X; T.tx = t.X * s.X;
T.ty = t.Y * s.Y; T.ty = t.Y * s.Y;
return T; return T;
@ -370,7 +364,7 @@ void phys2d_applypoly(struct phys2d_poly *poly) {
if (arrlen(poly->points) <= 0) return; if (arrlen(poly->points) <= 0) return;
struct gameobject *go = id2go(poly->shape.go); struct gameobject *go = id2go(poly->shape.go);
cpTransform T = trs2cpt((HMM_Vec2){0,0}, 0, (HMM_Vec2){1,1}); cpTransform T = trs2cpt((HMM_Vec2){0,0}, 0, go->scale.XY);
cpPolyShapeSetVerts(poly->shape.shape, arrlen(poly->points), poly->points, T); cpPolyShapeSetVerts(poly->shape.shape, arrlen(poly->points), poly->points, T);
cpPolyShapeSetRadius(poly->shape.shape, poly->radius); cpPolyShapeSetRadius(poly->shape.shape, poly->radius);
@ -384,9 +378,9 @@ void phys2d_dbgdrawpoly(struct phys2d_poly *poly) {
if (arrlen(poly->points) >= 3) { if (arrlen(poly->points) >= 3) {
int n = cpPolyShapeGetCount(poly->shape.shape); int n = cpPolyShapeGetCount(poly->shape.shape);
HMM_Vec2 points[n]; HMM_Vec2 points[n];
HMM_Mat3 rt = mt_rt(go2t(shape2go(poly->shape.shape)));
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
points[i] = bodytransformpoint(cpShapeGetBody(poly->shape.shape), cpPolyShapeGetVert(poly->shape.shape, i)); points[i] = mat_t_pos(rt, (HMM_Vec2)cpPolyShapeGetVert(poly->shape.shape, i));
draw_poly(points, n, color); draw_poly(points, n, color);
float seglen = cpShapeGetSensor(poly->shape.shape) ? sensor_seg : 0; float seglen = cpShapeGetSensor(poly->shape.shape) ? sensor_seg : 0;
@ -511,11 +505,11 @@ void phys2d_dbgdrawedge(struct phys2d_edge *edge) {
HMM_Vec2 drawpoints[arrlen(edge->points)]; HMM_Vec2 drawpoints[arrlen(edge->points)];
struct gameobject *go = id2go(edge->shape.go); struct gameobject *go = id2go(edge->shape.go);
for (int i = 0; i < arrlen(edge->points); i++) { HMM_Mat3 g2w = t_go2world(go);
drawpoints[i] = goscale(go, edge->points[i]); for (int i = 0; i < arrlen(edge->points); i++)
drawpoints[i] = bodytransformpoint(cpShapeGetBody(edge->shapes[0]), drawpoints[i].cp); drawpoints[i] = mat_t_pos(g2w, edge->points[i]);
}
float seglen = cpShapeGetSensor(edge->shapes[0]) ? sensor_seg : 0; float seglen = cpShapeGetSensor(edge->shapes[0]) ? sensor_seg : 0;
struct rgba color = shape_color(edge->shapes[0]); struct rgba color = shape_color(edge->shapes[0]);

View file

@ -64,7 +64,7 @@ struct phys2d_box {
/* An edge with no volume. Cannot collide with each other. Join to make levels. Static only. */ /* An edge with no volume. Cannot collide with each other. Join to make levels. Static only. */
struct phys2d_edge { struct phys2d_edge {
HMM_Vec2 *points; HMM_Vec2 *points; /* Points defined relative to the gameobject */
float thickness; float thickness;
cpShape **shapes; cpShape **shapes;
int closed; /* True if the first and last points should be connected */ int closed; /* True if the first and last points should be connected */

View file

@ -68,31 +68,57 @@ float go2angle(struct gameobject *go)
transform2d mat2transform2d(HMM_Mat3 mat) transform2d mat2transform2d(HMM_Mat3 mat)
{ {
}
HMM_Mat3 mt_t(transform2d t)
{
HMM_Mat3 p = HMM_M3D(1);
p.Columns[2].X = t.pos.X;
p.Columns[2].Y = t.pos.Y;
return p;
}
HMM_Mat3 mt_s(transform2d t)
{
HMM_Mat3 s = HMM_M3D(1);
s.Columns[0].X = t.scale.X;
s.Columns[1].Y = t.scale.Y;
return s;
}
HMM_Mat3 mt_r(transform2d t)
{
HMM_Mat3 r = HMM_M3D(1);
r.Columns[0] = (HMM_Vec3){cos(t.angle), sin(t.angle), 0};
r.Columns[1] = (HMM_Vec3){-sin(t.angle), cos(t.angle), 0};
return r;
} }
HMM_Mat3 transform2d2mat(transform2d t) HMM_Mat3 transform2d2mat(transform2d t)
{ {
HMM_Mat3 m = HMM_M3D(1); return HMM_MulM3(mt_t(t), HMM_MulM3(mt_r(t), mt_s(t)));
HMM_Mat3 p = HMM_M3D(1); }
p.Columns[2].X = t.pos.X;
p.Columns[2].Y = t.pos.Y;
HMM_Mat3 s = HMM_M3D(1); HMM_Mat3 mt_rst(transform2d t)
s.Columns[0].X = t.scale.X; {
s.Columns[1].Y = t.scale.Y; return transform2d2mat(t);
}
HMM_Mat3 r = HMM_M3D(1); HMM_Mat3 mt_st(transform2d t)
r.Columns[0] = (HMM_Vec3){cos(t.angle), sin(t.angle), 0}; {
r.Columns[1] = (HMM_Vec3){-sin(t.angle), cos(t.angle), 0}; return HMM_MulM3(mt_t(t), mt_s(t));
}
m = HMM_MulM3(r, s); HMM_Mat3 mt_rt(transform2d t)
m = HMM_MulM3(p, m); {
return m; return HMM_MulM3(mt_t(t), mt_r(t));
} }
HMM_Vec2 go2world(struct gameobject *go, HMM_Vec2 pos) HMM_Vec2 go2world(struct gameobject *go, HMM_Vec2 pos)
{ {
return HMM_MulM3V3(t_go2world(go), (HMM_Vec3){pos.X, pos.Y, 1.0}).XY; HMM_Vec2 v = HMM_MulM3V3(t_go2world(go), (HMM_Vec3){pos.X, pos.Y, 1.0}).XY;
return v;
} }
HMM_Vec2 world2go(struct gameobject *go, HMM_Vec2 pos) HMM_Vec2 world2go(struct gameobject *go, HMM_Vec2 pos)
@ -100,6 +126,17 @@ HMM_Vec2 world2go(struct gameobject *go, HMM_Vec2 pos)
return HMM_MulM3V3(t_world2go(go), (HMM_Vec3){pos.X, pos.Y, 1.0}).XY; return HMM_MulM3V3(t_world2go(go), (HMM_Vec3){pos.X, pos.Y, 1.0}).XY;
} }
HMM_Vec2 mat_t_pos(HMM_Mat3 m, HMM_Vec2 pos)
{
return HMM_MulM3V3(m, (HMM_Vec3){pos.x, pos.y, 1}).XY;
}
HMM_Vec2 mat_t_dir(HMM_Mat3 m, HMM_Vec2 dir)
{
m.Columns[2] = (HMM_Vec3){0,0,1};
return HMM_MulM3V3(m, (HMM_Vec3){dir.x, dir.y, 1}).XY;
}
HMM_Vec2 goscale(struct gameobject *go, HMM_Vec2 pos) HMM_Vec2 goscale(struct gameobject *go, HMM_Vec2 pos)
{ {
return HMM_MulV2(go->scale.XY, pos); return HMM_MulV2(go->scale.XY, pos);
@ -149,10 +186,11 @@ void gameobject_set_sensor(int id, int sensor) {
transform2d go2t(gameobject *go) transform2d go2t(gameobject *go)
{ {
transform2d t; transform2d t;
cpVect p = cpBodyGetPosition(go->body); t.pos.cp = cpBodyGetPosition(go->body);
t.pos.X = p.x; t.pos.Y = p.y;
t.angle = cpBodyGetAngle(go->body); t.angle = cpBodyGetAngle(go->body);
t.scale = go->scale.XY; t.scale = go->scale.XY;
if (isnan(t.scale.X)) t.scale.X = 1;
if (isnan(t.scale.Y)) t.scale.Y = 1;
return t; return t;
} }

View file

@ -66,6 +66,15 @@ HMM_Mat3 t_world2go(struct gameobject *go);
HMM_Vec2 goscale(struct gameobject *go, HMM_Vec2 pos); HMM_Vec2 goscale(struct gameobject *go, HMM_Vec2 pos);
HMM_Vec2 gotpos(struct gameobject *go, HMM_Vec2 pos); HMM_Vec2 gotpos(struct gameobject *go, HMM_Vec2 pos);
HMM_Mat3 mt_rst(transform2d t);
HMM_Mat3 mt_st(transform2d t);
HMM_Mat3 mt_rt(transform2d t);
/* Transform a position via the matrix */
HMM_Vec2 mat_t_pos(HMM_Mat3 m, HMM_Vec2 pos);
/* Transform a direction via the matrix - does not take into account translation of matrix */
HMM_Vec2 mat_t_dir(HMM_Mat3 m, HMM_Vec2 dir);
struct gameobject *get_gameobject_from_id(int id); struct gameobject *get_gameobject_from_id(int id);
struct gameobject *id2go(int id); struct gameobject *id2go(int id);
int id_from_gameobject(struct gameobject *go); int id_from_gameobject(struct gameobject *go);

View file

@ -271,12 +271,6 @@ JSValue vec2js(HMM_Vec2 v) {
return array; return array;
} }
JSValue v22js(HMM_Vec2 v)
{
HMM_Vec2 c = { v.X, v.Y };
return vec2js(c);
}
JSValue vecarr2js(HMM_Vec2 *points, int n) { JSValue vecarr2js(HMM_Vec2 *points, int n) {
JSValue array = JS_NewArray(js); JSValue array = JS_NewArray(js);
for (int i = 0; i < n; i++) for (int i = 0; i < n; i++)
@ -855,12 +849,12 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break; break;
case 87: case 87:
str = JS_ToCString(js, argv[1]); // str = JS_ToCString(js, argv[1]);
// mini_music_play(str); // mini_music_play(str);
break; break;
case 88: case 88:
// mini_music_pause(); ret = num2js(HMM_DotV2(js2vec2(argv[1]), js2vec2(argv[2])));
break; break;
case 89: case 89:
@ -926,7 +920,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break; break;
case 103: case 103:
ret = num2js(js2go(argv[1])->scale.X); ret = vec2js(js2go(argv[1])->scale.XY);
break; break;
case 104: case 104:
@ -958,7 +952,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break; break;
case 111: case 111:
ret = v22js(js2sprite(argv[1])->pos); ret = vec2js(js2sprite(argv[1])->pos);
break; break;
case 112: case 112:
@ -1062,11 +1056,11 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break; break;
case 136: case 136:
ret = v22js(world2screen(js2vec2(argv[1]))); ret = vec2js(world2screen(js2vec2(argv[1])));
break; break;
case 137: case 137:
ret = v22js(screen2world(js2vec2(argv[1]))); ret = vec2js(screen2world(js2vec2(argv[1])));
break; break;
case 138: case 138: