Compare commits
No commits in common. "f5a174710d2d4decad3088f881923cd8c863d588" and "9a53ecf4c968430947e8a0000fb76d9f028665f2" have entirely different histories.
f5a174710d
...
9a53ecf4c9
168
src/sat.ts
168
src/sat.ts
|
@ -33,79 +33,111 @@ class Vector {
|
||||||
this.y = y;
|
this.y = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
add(v2: Vector) {
|
add: (v2: Vector) {
|
||||||
return new Vector(this.x + v2.x, this.y + v2.y);
|
return new Vector(this.x + v2.x, this.y + v2.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
scalar(s: number) {
|
|
||||||
return new Vector(this.x * s, this.y * s);
|
|
||||||
}
|
|
||||||
|
|
||||||
dot(v2: Vector) {
|
|
||||||
return this.x * v2.x + this.y * v2.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
magnitude2() {
|
add: function (v1, v2) {
|
||||||
return this.x * this.x + this.y * this.y;
|
return {
|
||||||
}
|
x: v1.x + v2.x,
|
||||||
|
y: v1.y + v2.y,
|
||||||
magnitude() {
|
|
||||||
return Math.sqrt(this.magnitude2());
|
|
||||||
}
|
|
||||||
|
|
||||||
normal() {
|
|
||||||
const mag = this.magnitude();
|
|
||||||
return new Vector(this.x / mag, this.y / mag);
|
|
||||||
}
|
|
||||||
|
|
||||||
leftNormal() {
|
|
||||||
return new Vector(-1 * this.y, this.x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const vec = (x: number, y: number) => new Vector(x, y);
|
|
||||||
|
|
||||||
type Shape = Vector[];
|
|
||||||
|
|
||||||
type Projection = { min: number; max: number };
|
|
||||||
|
|
||||||
function colliding(shape1: Shape, shape2: Shape) {
|
|
||||||
const genAxes = (shape: Shape) => {
|
|
||||||
if (shape.length < 3) {
|
|
||||||
throw new Error("A Shape must be a polygon.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const axis = (idx: number) => {
|
|
||||||
const p1 = shape[idx];
|
|
||||||
const p2 = shape[idx === shape.length - 1 ? 0 : idx + 1];
|
|
||||||
return vec(p1.x - p2.x, p1.y - p2.y)
|
|
||||||
.normal()
|
|
||||||
.leftNormal();
|
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
scalar: function (v, s) {
|
||||||
|
return {
|
||||||
|
x: v.x * s,
|
||||||
|
y: v.y * s,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
dot: function (v1, v2) {
|
||||||
|
return v1.x * v2.x + v1.y * v2.y;
|
||||||
|
},
|
||||||
|
magnitude2: function (v) {
|
||||||
|
var x, y;
|
||||||
|
x = v.x;
|
||||||
|
y = v.y;
|
||||||
|
return x * x + y * y;
|
||||||
|
},
|
||||||
|
magnitude: function (v) {
|
||||||
|
return Math.sqrt(Math.vector.magnitude2(v));
|
||||||
|
},
|
||||||
|
normalize: function (v) {
|
||||||
|
var mag;
|
||||||
|
mag = Math.vector.magnitude(v);
|
||||||
|
return {
|
||||||
|
x: v.x / mag,
|
||||||
|
y: v.y / mag,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
leftNormal: function (v) {
|
||||||
|
return {
|
||||||
|
x: -v.y,
|
||||||
|
y: v.x,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
return shape.map((_: Vector, idx: number) => axis(idx));
|
this.colliding = function (shape1, shape2) {
|
||||||
|
var axes, axes1, axes2, axis, genAxes, genProjection, j, len, proj1, proj2;
|
||||||
|
genAxes = function (shape) {
|
||||||
|
var axis, i, j, ref, results;
|
||||||
|
if (shape.length < 3) {
|
||||||
|
throw "Cannot handle non-polygons";
|
||||||
|
}
|
||||||
|
axis = function (shape, pi) {
|
||||||
|
var edge, p1, p2;
|
||||||
|
p1 = shape[pi];
|
||||||
|
p2 = shape[pi === shape.length - 1 ? 0 : pi + 1];
|
||||||
|
edge = {
|
||||||
|
x: p1.x - p2.x,
|
||||||
|
y: p1.y - p2.y,
|
||||||
|
};
|
||||||
|
return Math.vector.normalize(Math.vector.leftNormal(edge));
|
||||||
|
};
|
||||||
|
results = [];
|
||||||
|
for (i = j = 0, ref = shape.length; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) {
|
||||||
|
results.push(axis(shape, i));
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
genProjection = function (shape, axis) {
|
||||||
|
var i, j, max, min, p, ref;
|
||||||
|
min = Math.vector.dot(axis, shape[0]);
|
||||||
|
max = min;
|
||||||
|
for (i = j = 1, ref = shape.length; 1 <= ref ? j < ref : j > ref; i = 1 <= ref ? ++j : --j) {
|
||||||
|
p = Math.vector.dot(axis, shape[i]);
|
||||||
|
if (p < min) {
|
||||||
|
min = p;
|
||||||
|
}
|
||||||
|
if (p > max) {
|
||||||
|
max = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
min: min,
|
||||||
|
max: max,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
axes1 = genAxes(shape1);
|
||||||
|
axes2 = genAxes(shape2);
|
||||||
|
axes = axes1.concat(axes2);
|
||||||
|
for (j = 0, len = axes.length; j < len; j++) {
|
||||||
|
axis = axes[j];
|
||||||
|
proj1 = genProjection(shape1, axis);
|
||||||
|
proj2 = genProjection(shape2, axis);
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
(proj1.min >= proj2.min && proj1.min <= proj2.max) ||
|
||||||
|
(proj1.max >= proj2.min && proj1.max <= proj2.max) ||
|
||||||
|
(proj2.min >= proj1.min && proj2.min <= proj1.max) ||
|
||||||
|
(proj2.max >= proj1.min && proj2.max <= proj1.max)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
}).call(this);
|
||||||
const genProjection = (shape: Shape, axis: Vector) =>
|
|
||||||
shape.reduce(
|
|
||||||
({ min, max }: Projection, v: Vector) => {
|
|
||||||
const p = axis.dot(v);
|
|
||||||
return { min: p < min ? p : min, max: p > max ? p : max };
|
|
||||||
},
|
|
||||||
{ min: axis.dot(shape[0]), max: axis.dot(shape[0]) }
|
|
||||||
);
|
|
||||||
|
|
||||||
const axes = [...genAxes(shape1), ...genAxes(shape2)];
|
|
||||||
|
|
||||||
// The logic here may be wrong. I may have to invert the return on the whole expression.
|
|
||||||
return axes.some((axis) => {
|
|
||||||
const proj1 = genProjection(shape1, axis);
|
|
||||||
const proj2 = genProjection(shape2, axis);
|
|
||||||
return !(
|
|
||||||
(proj1.min >= proj2.min && proj1.min <= proj2.max) ||
|
|
||||||
(proj1.max >= proj2.min && proj1.max <= proj2.max) ||
|
|
||||||
(proj2.min >= proj1.min && proj2.min <= proj1.max) ||
|
|
||||||
(proj2.max >= proj1.min && proj2.max <= proj1.max)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue