module exports

This commit is contained in:
Emmaline Autumn 2023-11-05 02:04:18 -07:00
parent ea311a1787
commit 69ab471d22
3 changed files with 204 additions and 180 deletions

363
bundle.js
View File

@ -684,6 +684,184 @@ class OriginVector extends Vector {
return new OriginVector(origin, v);
}
}
class SplineSegment {
points;
length;
constructor(points){
this.points = points;
this.length = this.calculateApproxLength(100);
}
draw(color) {
const [a, b, c, d] = this.points;
doodler.drawBezier(a, b, c, d, {
strokeColor: color || "#ffffff50"
});
}
getPointAtT(t) {
const [a, b, c, d] = this.points;
const res = a.copy();
res.add(Vector.add(a.copy().mult(-3), b.copy().mult(3)).mult(t));
res.add(Vector.add(Vector.add(a.copy().mult(3), b.copy().mult(-6)), c.copy().mult(3)).mult(Math.pow(t, 2)));
res.add(Vector.add(Vector.add(a.copy().mult(-1), b.copy().mult(3)), Vector.add(c.copy().mult(-3), d.copy())).mult(Math.pow(t, 3)));
return res;
}
getClosestPoint(v) {
const resolution = 1 / 25;
let closest = this.points[0];
let closestDistance = this.points[0].dist(v);
let closestT = 0;
for(let i = 0; i < 25; i++){
const point = this.getPointAtT(i * resolution);
const distance = v.dist(point);
if (distance < closestDistance) {
closest = point;
closestDistance = distance;
closestT = i * resolution;
}
}
return [
closest,
closestDistance,
closestT
];
}
getPointsWithinRadius(v, r) {
const points = [];
const resolution = 1 / 25;
for(let i = 0; i < 25 + 1; i++){
const point = this.getPointAtT(i * resolution);
const distance = v.dist(point);
if (distance < r) {
points.push([
i * resolution,
this
]);
}
}
return points;
}
tangent(t) {
const [a, b, c, d] = this.points;
const res = Vector.sub(b, a).mult(3 * Math.pow(1 - t, 2));
res.add(Vector.add(Vector.sub(c, b).mult(6 * (1 - t) * t), Vector.sub(d, c).mult(3 * Math.pow(t, 2))));
return res;
}
doesIntersectCircle(x, y, r) {
const v = new Vector(x, y);
const resolution = 1 / 25;
let distance = Infinity;
let t;
for(let i = 0; i < 25 - 1; i++){
const a = this.getPointAtT(i * resolution);
const b = this.getPointAtT((i + 1) * resolution);
const ac = Vector.sub(v, a);
const ab = Vector.sub(b, a);
const d = Vector.add(Vector.vectorProjection(ac, ab), a);
const ad = Vector.sub(d, a);
const k = Math.abs(ab.x) > Math.abs(ab.y) ? ad.x / ab.x : ad.y / ab.y;
let dist;
if (k <= 0.0) {
dist = Vector.hypot2(v, a);
} else if (k >= 1.0) {
dist = Vector.hypot2(v, b);
}
dist = Vector.hypot2(v, d);
if (dist < distance) {
distance = dist;
t = i * resolution;
}
}
if (distance < r) return t;
return false;
}
intersectsCircle(circleCenter, radius) {
for(let i = 0; i < 100; i++){
const t1 = i / 100;
const t2 = (i + 1) / 100;
const segmentStart = this.getPointAtT(t1);
const segmentEnd = this.getPointAtT(t2);
const segmentLength = Math.sqrt((segmentEnd.x - segmentStart.x) ** 2 + (segmentEnd.y - segmentStart.y) ** 2);
const resolution = Math.max(10, Math.ceil(100 * (segmentLength / radius)));
for(let j = 0; j <= resolution; j++){
const t = j / resolution;
const point = this.getPointAtT(t);
const distance = Math.sqrt((point.x - circleCenter.x) ** 2 + (point.y - circleCenter.y) ** 2);
if (distance <= radius) {
return true;
}
}
}
return false;
}
calculateApproxLength(resolution = 25) {
const stepSize = 1 / resolution;
const points = [];
for(let i = 0; i <= resolution; i++){
const current = stepSize * i;
points.push(this.getPointAtT(current));
}
this.length = points.reduce((acc, cur)=>{
const prev = acc.prev;
acc.prev = cur;
if (!prev) return acc;
acc.length += cur.dist(prev);
return acc;
}, {
prev: undefined,
length: 0
}).length;
return this.length;
}
calculateEvenlySpacedPoints(spacing, resolution = 1) {
const points = [];
points.push(this.points[0]);
let prev = points[0];
let distSinceLastEvenPoint = 0;
let t = 0;
const div = Math.ceil(this.length * resolution * 10);
while(t < 1){
t += 1 / div;
const point = this.getPointAtT(t);
distSinceLastEvenPoint += prev.dist(point);
if (distSinceLastEvenPoint >= spacing) {
const overshoot = distSinceLastEvenPoint - spacing;
const evenPoint = Vector.add(point, Vector.sub(point, prev).normalize().mult(overshoot));
distSinceLastEvenPoint = overshoot;
points.push(evenPoint);
prev = evenPoint;
}
prev = point;
}
return points;
}
_aabb;
get AABB() {
if (!this._aabb) {
this._aabb = this.recalculateAABB();
}
return this._aabb;
}
recalculateAABB() {
let minX = Infinity;
let minY = Infinity;
let maxX = -Infinity;
let maxY = -Infinity;
for(let i = 0; i < 100; i++){
const t = i / 100;
const point = this.getPointAtT(t);
minX = Math.min(minX, point.x);
minY = Math.min(minY, point.y);
maxX = Math.max(maxX, point.x);
maxY = Math.max(maxY, point.y);
}
return {
x: minX,
y: minY,
w: maxX - minX,
h: maxY - minY
};
}
}
class Doodler {
ctx;
_canvas;
@ -1292,6 +1470,10 @@ class Polygon {
color
});
}
doodler.dot(this.center, {
weight: 4,
color: "yellow"
});
}
calcCenter() {
if (!this.points.length) return new Vector();
@ -1354,6 +1536,9 @@ class Polygon {
poly.points.push(pt);
}
poly.center = poly.calcCenter();
for (const p of poly.points){
p.sub(poly.center);
}
return poly;
}
getEdges() {
@ -1413,184 +1598,6 @@ function projectCircleOntoAxis(c, axis) {
function overlap(proj1, proj2) {
return proj1.min <= proj2.max && proj1.max >= proj2.min;
}
class SplineSegment {
points;
length;
constructor(points){
this.points = points;
this.length = this.calculateApproxLength(100);
}
draw(color) {
const [a, b, c, d] = this.points;
doodler.drawBezier(a, b, c, d, {
strokeColor: color || "#ffffff50"
});
}
getPointAtT(t) {
const [a, b, c, d] = this.points;
const res = a.copy();
res.add(Vector.add(a.copy().mult(-3), b.copy().mult(3)).mult(t));
res.add(Vector.add(Vector.add(a.copy().mult(3), b.copy().mult(-6)), c.copy().mult(3)).mult(Math.pow(t, 2)));
res.add(Vector.add(Vector.add(a.copy().mult(-1), b.copy().mult(3)), Vector.add(c.copy().mult(-3), d.copy())).mult(Math.pow(t, 3)));
return res;
}
getClosestPoint(v) {
const resolution = 1 / 25;
let closest = this.points[0];
let closestDistance = this.points[0].dist(v);
let closestT = 0;
for(let i = 0; i < 25; i++){
const point = this.getPointAtT(i * resolution);
const distance = v.dist(point);
if (distance < closestDistance) {
closest = point;
closestDistance = distance;
closestT = i * resolution;
}
}
return [
closest,
closestDistance,
closestT
];
}
getPointsWithinRadius(v, r) {
const points = [];
const resolution = 1 / 25;
for(let i = 0; i < 25 + 1; i++){
const point = this.getPointAtT(i * resolution);
const distance = v.dist(point);
if (distance < r) {
points.push([
i * resolution,
this
]);
}
}
return points;
}
tangent(t) {
const [a, b, c, d] = this.points;
const res = Vector.sub(b, a).mult(3 * Math.pow(1 - t, 2));
res.add(Vector.add(Vector.sub(c, b).mult(6 * (1 - t) * t), Vector.sub(d, c).mult(3 * Math.pow(t, 2))));
return res;
}
doesIntersectCircle(x, y, r) {
const v = new Vector(x, y);
const resolution = 1 / 25;
let distance = Infinity;
let t;
for(let i = 0; i < 25 - 1; i++){
const a = this.getPointAtT(i * resolution);
const b = this.getPointAtT((i + 1) * resolution);
const ac = Vector.sub(v, a);
const ab = Vector.sub(b, a);
const d = Vector.add(Vector.vectorProjection(ac, ab), a);
const ad = Vector.sub(d, a);
const k = Math.abs(ab.x) > Math.abs(ab.y) ? ad.x / ab.x : ad.y / ab.y;
let dist;
if (k <= 0.0) {
dist = Vector.hypot2(v, a);
} else if (k >= 1.0) {
dist = Vector.hypot2(v, b);
}
dist = Vector.hypot2(v, d);
if (dist < distance) {
distance = dist;
t = i * resolution;
}
}
if (distance < r) return t;
return false;
}
intersectsCircle(circleCenter, radius) {
for(let i = 0; i < 100; i++){
const t1 = i / 100;
const t2 = (i + 1) / 100;
const segmentStart = this.getPointAtT(t1);
const segmentEnd = this.getPointAtT(t2);
const segmentLength = Math.sqrt((segmentEnd.x - segmentStart.x) ** 2 + (segmentEnd.y - segmentStart.y) ** 2);
const resolution = Math.max(10, Math.ceil(100 * (segmentLength / radius)));
for(let j = 0; j <= resolution; j++){
const t = j / resolution;
const point = this.getPointAtT(t);
const distance = Math.sqrt((point.x - circleCenter.x) ** 2 + (point.y - circleCenter.y) ** 2);
if (distance <= radius) {
return true;
}
}
}
return false;
}
calculateApproxLength(resolution = 25) {
const stepSize = 1 / resolution;
const points = [];
for(let i = 0; i <= resolution; i++){
const current = stepSize * i;
points.push(this.getPointAtT(current));
}
this.length = points.reduce((acc, cur)=>{
const prev = acc.prev;
acc.prev = cur;
if (!prev) return acc;
acc.length += cur.dist(prev);
return acc;
}, {
prev: undefined,
length: 0
}).length;
return this.length;
}
calculateEvenlySpacedPoints(spacing, resolution = 1) {
const points = [];
points.push(this.points[0]);
let prev = points[0];
let distSinceLastEvenPoint = 0;
let t = 0;
const div = Math.ceil(this.length * resolution * 10);
while(t < 1){
t += 1 / div;
const point = this.getPointAtT(t);
distSinceLastEvenPoint += prev.dist(point);
if (distSinceLastEvenPoint >= spacing) {
const overshoot = distSinceLastEvenPoint - spacing;
const evenPoint = Vector.add(point, Vector.sub(point, prev).normalize().mult(overshoot));
distSinceLastEvenPoint = overshoot;
points.push(evenPoint);
prev = evenPoint;
}
prev = point;
}
return points;
}
_aabb;
get AABB() {
if (!this._aabb) {
this._aabb = this.recalculateAABB();
}
return this._aabb;
}
recalculateAABB() {
let minX = Infinity;
let minY = Infinity;
let maxX = -Infinity;
let maxY = -Infinity;
for(let i = 0; i < 100; i++){
const t = i / 100;
const point = this.getPointAtT(t);
minX = Math.min(minX, point.x);
minY = Math.min(minY, point.y);
maxX = Math.max(maxX, point.x);
maxY = Math.max(maxY, point.y);
}
return {
x: minX,
y: minY,
w: maxX - minX,
h: maxY - minY
};
}
}
init({
fillScreen: true,
bg: "#333",

View File

@ -20,6 +20,8 @@ export class Polygon {
color,
});
}
doodler.dot(this.center, { weight: 4, color: "yellow" });
}
calcCenter() {
@ -103,6 +105,9 @@ export class Polygon {
poly.points.push(pt);
}
poly.center = poly.calcCenter();
for (const p of poly.points) {
p.sub(poly.center);
}
return poly;
}

16
mod.ts
View File

@ -1,5 +1,17 @@
/// <reference types="./global.d.ts" />
export { init as initializeDoodler } from "./init.ts";
export { GIFAnimation } from "./animation/gif.ts";
export { SpriteAnimation } from "./animation/sprite.ts";
export { axisAlignedCollision, axisAlignedContains } from "./collision/aa.ts";
export { circularCollision } from "./collision/circular.ts";
export {
satCollisionAABBCircle,
satCollisionCircle,
satCollisionPolygon,
satCollisionSpline,
} from "./collision/sat.ts";
export { Vector } from "./geometry/vector.ts";
export { Polygon } from "./geometry/polygon.ts";
export { SplineSegment } from "./geometry/spline.ts";
export { init as initializeDoodler } from "./init.ts";