feat: vertice transformation and collider aabb updates
This commit is contained in:
parent
b9985a573d
commit
557c8285ba
|
@ -1,5 +1,5 @@
|
|||
import Component from '@/ecs/component.js';
|
||||
import {distance, intersects} from '@/util/math.js';
|
||||
import {distance, intersects, transform} from '@/util/math.js';
|
||||
|
||||
import vector2d from './helpers/vector-2d';
|
||||
|
||||
|
@ -7,6 +7,8 @@ export default class Collider extends Component {
|
|||
instanceFromSchema() {
|
||||
const {ecs} = this;
|
||||
return class ColliderInstance extends super.instanceFromSchema() {
|
||||
$$aabb = {x0: Infinity, x1: -Infinity, y0: Infinity, y1: -Infinity};
|
||||
$$aabbs = [];
|
||||
collidingWith = {};
|
||||
get aabb() {
|
||||
const {Position: {x: px, y: py}} = ecs.get(this.entity);
|
||||
|
@ -76,34 +78,36 @@ export default class Collider extends Component {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
async load(instance) {
|
||||
instance.$$aabb = {x0: Infinity, x1: -Infinity, y0: Infinity, y1: -Infinity};
|
||||
instance.$$aabbs = [];
|
||||
const {bodies} = instance;
|
||||
updateAabbs() {
|
||||
this.$$aabb = {x0: Infinity, x1: -Infinity, y0: Infinity, y1: -Infinity};
|
||||
this.$$aabbs = [];
|
||||
const {bodies} = this;
|
||||
const {Direction: {direction = 0} = {}} = ecs.get(this.entity);
|
||||
for (const body of bodies) {
|
||||
let x0 = Infinity, x1 = -Infinity, y0 = Infinity, y1 = -Infinity;
|
||||
for (const point of body.points) {
|
||||
const x = point.x;
|
||||
const y = point.y;
|
||||
for (const point of transform(body.points, {rotation: direction})) {
|
||||
const {x, y} = point;
|
||||
if (x < x0) x0 = x;
|
||||
if (x < instance.$$aabb.x0) instance.$$aabb.x0 = x;
|
||||
if (x < this.$$aabb.x0) this.$$aabb.x0 = x;
|
||||
if (x > x1) x1 = x;
|
||||
if (x > instance.$$aabb.x1) instance.$$aabb.x1 = x;
|
||||
if (x > this.$$aabb.x1) this.$$aabb.x1 = x;
|
||||
if (y < y0) y0 = y;
|
||||
if (y < instance.$$aabb.y0) instance.$$aabb.y0 = y;
|
||||
if (y < this.$$aabb.y0) this.$$aabb.y0 = y;
|
||||
if (y > y1) y1 = y;
|
||||
if (y > instance.$$aabb.y1) instance.$$aabb.y1 = y;
|
||||
if (y > this.$$aabb.y1) this.$$aabb.y1 = y;
|
||||
}
|
||||
instance.$$aabbs.push({
|
||||
this.$$aabbs.push({
|
||||
x0: x0 > x1 ? x1 : x0,
|
||||
x1: x0 > x1 ? x0 : x1,
|
||||
y0: y0 > y1 ? y1 : y0,
|
||||
y1: y0 > y1 ? y0 : y1,
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
async load(instance) {
|
||||
instance.updateAabbs();
|
||||
// heavy handed...
|
||||
if ('undefined' !== typeof window) {
|
||||
return;
|
||||
|
|
|
@ -40,7 +40,16 @@ export default class Colliders extends System {
|
|||
|
||||
tick() {
|
||||
const collisions = new Map();
|
||||
for (const entity of this.ecs.changed(['Direction'])) {
|
||||
if (!entity.Collider) {
|
||||
continue;
|
||||
}
|
||||
entity.Collider.updateAabbs();
|
||||
}
|
||||
for (const entity of this.ecs.changed(['Position'])) {
|
||||
if (!entity.Collider) {
|
||||
continue;
|
||||
}
|
||||
this.updateHash(entity);
|
||||
}
|
||||
for (const entity of this.ecs.changed(['Position'])) {
|
||||
|
|
|
@ -390,6 +390,9 @@ function Ui({disconnected}) {
|
|||
if (!update) {
|
||||
continue;
|
||||
}
|
||||
if (update.Direction && entity.Collider) {
|
||||
entity.Collider.updateAabbs();
|
||||
}
|
||||
if (update.Sound?.play) {
|
||||
for (const sound of update.Sound.play) {
|
||||
(new Audio(sound)).play();
|
||||
|
|
|
@ -316,3 +316,52 @@ export function removeCollinear([...vertices]) {
|
|||
}
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
export function transform(
|
||||
vertices,
|
||||
{
|
||||
rotation = 0,
|
||||
scale = 1,
|
||||
translation = {x: 0, y: 0},
|
||||
origin = {x: 0, y: 0},
|
||||
},
|
||||
) {
|
||||
// nop
|
||||
if (0 === rotation && 1 === scale && 0 === translation.x && 0 === translation.y) {
|
||||
return vertices;
|
||||
}
|
||||
const transformed = [];
|
||||
// scale
|
||||
for (const vertice of vertices) {
|
||||
if (1 === scale) {
|
||||
transformed.push({x: vertice.x, y: vertice.y});
|
||||
continue;
|
||||
}
|
||||
transformed.push({
|
||||
x: origin.x + (vertice.x - origin.x) * scale,
|
||||
y: origin.y + (vertice.y - origin.y) * scale,
|
||||
});
|
||||
}
|
||||
// rotation
|
||||
rotation = rotation % TAU;
|
||||
if (0 !== rotation) {
|
||||
for (const vertice of transformed) {
|
||||
let a = rotation + Math.atan2(
|
||||
vertice.y - origin.y,
|
||||
vertice.x - origin.x,
|
||||
);
|
||||
a = (a >= 0 || a < TAU) ? a : (a % TAU + TAU) % TAU;
|
||||
const d = distance(vertice, origin);
|
||||
vertice.x = origin.x + d * Math.cos(a);
|
||||
vertice.y = origin.y + d * Math.sin(a);
|
||||
}
|
||||
}
|
||||
// translation
|
||||
if (0 !== translation.x || 0 !== translation.y) {
|
||||
for (const vertice of transformed) {
|
||||
vertice.x += translation.x;
|
||||
vertice.y += translation.y;
|
||||
}
|
||||
}
|
||||
return transformed;
|
||||
}
|
||||
|
|
87
app/util/math.test.js
Normal file
87
app/util/math.test.js
Normal file
|
@ -0,0 +1,87 @@
|
|||
import {expect, test} from 'vitest';
|
||||
|
||||
import * as MathUtil from './math.js';
|
||||
|
||||
test('transforms vertices', async () => {
|
||||
const expectCloseTo = (l, r) => {
|
||||
expect(l.length)
|
||||
.to.equal(r.length);
|
||||
for (let i = 0; i < l.length; ++i) {
|
||||
expect(l[i].x)
|
||||
.to.be.closeTo(r[i].x, 0.0001);
|
||||
expect(l[i].y)
|
||||
.to.be.closeTo(r[i].y, 0.0001);
|
||||
}
|
||||
}
|
||||
const vertices = [
|
||||
{x: -1, y: -1},
|
||||
{x: 1, y: -1},
|
||||
{x: 1, y: 1},
|
||||
{x: -1, y: 1},
|
||||
];
|
||||
expect(MathUtil.transform(vertices, {}))
|
||||
.to.deep.equal(vertices);
|
||||
expectCloseTo(
|
||||
MathUtil.transform(vertices, {scale: 2}),
|
||||
[
|
||||
{x: -2, y: -2},
|
||||
{x: 2, y: -2},
|
||||
{x: 2, y: 2},
|
||||
{x: -2, y: 2},
|
||||
],
|
||||
);
|
||||
expectCloseTo(
|
||||
MathUtil.transform(vertices, {rotation: MathUtil.QUARTER_PI}),
|
||||
[
|
||||
{x: 0, y: -Math.sqrt(2)},
|
||||
{x: Math.sqrt(2), y: 0},
|
||||
{x: 0, y: Math.sqrt(2)},
|
||||
{x: -Math.sqrt(2), y: 0},
|
||||
],
|
||||
);
|
||||
expectCloseTo(
|
||||
MathUtil.transform(vertices, {rotation: MathUtil.QUARTER_PI, scale: 2}),
|
||||
[
|
||||
{x: 0, y: -Math.sqrt(2) * 2},
|
||||
{x: Math.sqrt(2) * 2, y: 0},
|
||||
{x: 0, y: Math.sqrt(2) * 2},
|
||||
{x: -Math.sqrt(2) * 2, y: 0},
|
||||
],
|
||||
);
|
||||
expectCloseTo(
|
||||
MathUtil.transform(vertices, {translation: {x: 10, y: 10}}),
|
||||
[
|
||||
{x: 9, y: 9},
|
||||
{x: 11, y: 9},
|
||||
{x: 11, y: 11},
|
||||
{x: 9, y: 11},
|
||||
],
|
||||
);
|
||||
expectCloseTo(
|
||||
MathUtil.transform(vertices, {rotation: MathUtil.QUARTER_PI, translation: {x: 10, y: 10}}),
|
||||
[
|
||||
{x: 10, y: 10 - Math.sqrt(2)},
|
||||
{x: 10 + Math.sqrt(2), y: 10},
|
||||
{x: 10, y: 10 + Math.sqrt(2)},
|
||||
{x: 10 - Math.sqrt(2), y: 10},
|
||||
],
|
||||
);
|
||||
expectCloseTo(
|
||||
MathUtil.transform(vertices, {scale: 2, translation: {x: 10, y: 10}}),
|
||||
[
|
||||
{x: 8, y: 8},
|
||||
{x: 12, y: 8},
|
||||
{x: 12, y: 12},
|
||||
{x: 8, y: 12},
|
||||
],
|
||||
);
|
||||
expectCloseTo(
|
||||
MathUtil.transform(vertices, {rotation: MathUtil.QUARTER_PI, scale: 2, translation: {x: 10, y: 10}}),
|
||||
[
|
||||
{x: 10, y: 10 - Math.sqrt(2) * 2},
|
||||
{x: 10 + Math.sqrt(2) * 2, y: 10},
|
||||
{x: 10, y: 10 + Math.sqrt(2) * 2},
|
||||
{x: 10 - Math.sqrt(2) * 2, y: 10},
|
||||
],
|
||||
);
|
||||
});
|
Loading…
Reference in New Issue
Block a user