Fixes circular SAT
This commit is contained in:
parent
e70787260a
commit
da77aa10bb
152
bundle.js
152
bundle.js
@ -707,7 +707,7 @@ class Doodler {
|
|||||||
document.body.append(canvas);
|
document.body.append(canvas);
|
||||||
}
|
}
|
||||||
this.bg = bg || "white";
|
this.bg = bg || "white";
|
||||||
this.framerate = framerate || 60;
|
this.framerate = framerate;
|
||||||
canvas.width = fillScreen ? document.body.clientWidth : width;
|
canvas.width = fillScreen ? document.body.clientWidth : width;
|
||||||
canvas.height = fillScreen ? document.body.clientHeight : height;
|
canvas.height = fillScreen ? document.body.clientHeight : height;
|
||||||
if (fillScreen) {
|
if (fillScreen) {
|
||||||
@ -735,10 +735,17 @@ class Doodler {
|
|||||||
lastFrameAt = 0;
|
lastFrameAt = 0;
|
||||||
startDrawLoop() {
|
startDrawLoop() {
|
||||||
this.lastFrameAt = Date.now();
|
this.lastFrameAt = Date.now();
|
||||||
this.timer = setInterval(()=>this.draw(), 1000 / this.framerate);
|
if (this.framerate) {
|
||||||
|
this.timer = setInterval(()=>this.draw(Date.now()), 1000 / this.framerate);
|
||||||
|
} else {
|
||||||
|
const cb = (t)=>{
|
||||||
|
this.draw(t);
|
||||||
|
requestAnimationFrame(cb);
|
||||||
|
};
|
||||||
|
requestAnimationFrame(cb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
draw() {
|
draw(time) {
|
||||||
const time = Date.now();
|
|
||||||
const frameTime = time - this.lastFrameAt;
|
const frameTime = time - this.lastFrameAt;
|
||||||
this.ctx.clearRect(0, 0, this.width, this.height);
|
this.ctx.clearRect(0, 0, this.width, this.height);
|
||||||
this.ctx.fillStyle = this.bg;
|
this.ctx.fillStyle = this.bg;
|
||||||
@ -1033,6 +1040,7 @@ class ZoomableDoodler extends Doodler {
|
|||||||
y: 0
|
y: 0
|
||||||
};
|
};
|
||||||
maxScale = 4;
|
maxScale = 4;
|
||||||
|
minScale = 1;
|
||||||
constructor(options, postInit){
|
constructor(options, postInit){
|
||||||
super(options, postInit);
|
super(options, postInit);
|
||||||
this._canvas.addEventListener("wheel", (e)=>{
|
this._canvas.addEventListener("wheel", (e)=>{
|
||||||
@ -1180,7 +1188,7 @@ class ZoomableDoodler extends Doodler {
|
|||||||
}, scaleBy);
|
}, scaleBy);
|
||||||
}
|
}
|
||||||
scaleAt(p, scaleBy) {
|
scaleAt(p, scaleBy) {
|
||||||
this.scale = Math.min(Math.max(this.scale * scaleBy, 1), this.maxScale);
|
this.scale = Math.min(Math.max(this.scale * scaleBy, this.minScale), this.maxScale);
|
||||||
this.origin.x = p.x - (p.x - this.origin.x) * scaleBy;
|
this.origin.x = p.x - (p.x - this.origin.x) * scaleBy;
|
||||||
this.origin.y = p.y - (p.y - this.origin.y) * scaleBy;
|
this.origin.y = p.y - (p.y - this.origin.y) * scaleBy;
|
||||||
this.constrainOrigin();
|
this.constrainOrigin();
|
||||||
@ -1205,12 +1213,12 @@ class ZoomableDoodler extends Doodler {
|
|||||||
this.origin.x = Math.min(Math.max(this.origin.x, -this._canvas.width * this.scale + this._canvas.width), 0);
|
this.origin.x = Math.min(Math.max(this.origin.x, -this._canvas.width * this.scale + this._canvas.width), 0);
|
||||||
this.origin.y = Math.min(Math.max(this.origin.y, -this._canvas.height * this.scale + this._canvas.height), 0);
|
this.origin.y = Math.min(Math.max(this.origin.y, -this._canvas.height * this.scale + this._canvas.height), 0);
|
||||||
}
|
}
|
||||||
draw() {
|
draw(time) {
|
||||||
this.ctx.setTransform(this.scale, 0, 0, this.scale, this.origin.x, this.origin.y);
|
this.ctx.setTransform(this.scale, 0, 0, this.scale, this.origin.x, this.origin.y);
|
||||||
this.animateZoom();
|
this.animateZoom();
|
||||||
this.ctx.fillStyle = this.bg;
|
this.ctx.fillStyle = this.bg;
|
||||||
this.ctx.fillRect(0, 0, this.width / this.scale, this.height / this.scale);
|
this.ctx.fillRect(0, 0, this.width / this.scale, this.height / this.scale);
|
||||||
super.draw();
|
super.draw(time);
|
||||||
}
|
}
|
||||||
getTouchOffset(p) {
|
getTouchOffset(p) {
|
||||||
const { x, y } = this._canvas.getBoundingClientRect();
|
const { x, y } = this._canvas.getBoundingClientRect();
|
||||||
@ -1269,54 +1277,6 @@ const init = (opt, zoomable, postInit)=>{
|
|||||||
window.doodler = zoomable ? new ZoomableDoodler(opt, postInit) : new Doodler(opt, postInit);
|
window.doodler = zoomable ? new ZoomableDoodler(opt, postInit) : new Doodler(opt, postInit);
|
||||||
window.doodler.init();
|
window.doodler.init();
|
||||||
};
|
};
|
||||||
function satCollisionSpline(p, spline) {
|
|
||||||
for(let i = 0; i < 100; i++){
|
|
||||||
const t1 = i / 100;
|
|
||||||
const t2 = (i + 1) / 100;
|
|
||||||
const segmentStart = spline.getPointAtT(t1);
|
|
||||||
const segmentEnd = spline.getPointAtT(t2);
|
|
||||||
if (segmentIntersectsPolygon(p, segmentStart, segmentEnd)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
function segmentIntersectsPolygon(p, start, end) {
|
|
||||||
const edges = p.getEdges();
|
|
||||||
for (const edge of edges){
|
|
||||||
const axis = edge.copy().normal().normalize();
|
|
||||||
const proj1 = projectPolygonOntoAxis(p, axis);
|
|
||||||
const proj2 = projectSegmentOntoAxis(start, end, axis);
|
|
||||||
if (!overlap(proj1, proj2)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
function projectPolygonOntoAxis(p, axis) {
|
|
||||||
let min = Infinity;
|
|
||||||
let max = -Infinity;
|
|
||||||
for (const point of p.points){
|
|
||||||
const dotProduct = point.copy().add(p.center).dot(axis);
|
|
||||||
min = Math.min(min, dotProduct);
|
|
||||||
max = Math.max(max, dotProduct);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
min,
|
|
||||||
max
|
|
||||||
};
|
|
||||||
}
|
|
||||||
function projectSegmentOntoAxis(start, end, axis) {
|
|
||||||
const dotProductStart = start.dot(axis);
|
|
||||||
const dotProductEnd = end.dot(axis);
|
|
||||||
return {
|
|
||||||
min: Math.min(dotProductStart, dotProductEnd),
|
|
||||||
max: Math.max(dotProductStart, dotProductEnd)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
function overlap(proj1, proj2) {
|
|
||||||
return proj1.min <= proj2.max && proj1.max >= proj2.min;
|
|
||||||
}
|
|
||||||
class Polygon {
|
class Polygon {
|
||||||
points;
|
points;
|
||||||
center;
|
center;
|
||||||
@ -1342,7 +1302,12 @@ class Polygon {
|
|||||||
center.div(this.points.length);
|
center.div(this.points.length);
|
||||||
return center;
|
return center;
|
||||||
}
|
}
|
||||||
get circularHitbox() {
|
_circularBoundingBox;
|
||||||
|
get circularBoundingBox() {
|
||||||
|
this._circularBoundingBox = this.calculateCircularBoundingBox();
|
||||||
|
return this._circularBoundingBox;
|
||||||
|
}
|
||||||
|
calculateCircularBoundingBox() {
|
||||||
let greatestDistance = 0;
|
let greatestDistance = 0;
|
||||||
for (const p of this.points){
|
for (const p of this.points){
|
||||||
greatestDistance = Math.max(p.copy().add(this.center).dist(this.center), greatestDistance);
|
greatestDistance = Math.max(p.copy().add(this.center).dist(this.center), greatestDistance);
|
||||||
@ -1354,9 +1319,7 @@ class Polygon {
|
|||||||
}
|
}
|
||||||
_aabb;
|
_aabb;
|
||||||
get AABB() {
|
get AABB() {
|
||||||
if (!this._aabb) {
|
this._aabb = this.recalculateAABB();
|
||||||
this._aabb = this.recalculateAABB();
|
|
||||||
}
|
|
||||||
return this._aabb;
|
return this._aabb;
|
||||||
}
|
}
|
||||||
recalculateAABB() {
|
recalculateAABB() {
|
||||||
@ -1371,8 +1334,8 @@ class Polygon {
|
|||||||
biggestY = Math.max(temp.y, biggestY);
|
biggestY = Math.max(temp.y, biggestY);
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
x: smallestX,
|
x: smallestX + this.center.x,
|
||||||
y: smallestY,
|
y: smallestY + this.center.y,
|
||||||
w: biggestX - smallestX,
|
w: biggestX - smallestX,
|
||||||
h: biggestY - smallestY
|
h: biggestY - smallestY
|
||||||
};
|
};
|
||||||
@ -1407,9 +1370,49 @@ class Polygon {
|
|||||||
for (const point of this.points){
|
for (const point of this.points){
|
||||||
if (p.dist(point) < p.dist(nearest)) nearest = point;
|
if (p.dist(point) < p.dist(nearest)) nearest = point;
|
||||||
}
|
}
|
||||||
return nearest;
|
return nearest.copy().add(this.center);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function satCollisionCircle(p, circle) {
|
||||||
|
for (const edge of p.getEdges()){
|
||||||
|
const axis = edge.copy().normal().normalize();
|
||||||
|
const proj1 = projectPolygonOntoAxis(p, axis);
|
||||||
|
const proj2 = projectCircleOntoAxis(circle, axis);
|
||||||
|
if (!overlap(proj1, proj2)) return false;
|
||||||
|
}
|
||||||
|
const center = new Vector(circle.center);
|
||||||
|
const nearest = p.getNearestPoint(center);
|
||||||
|
const axis = nearest.copy().sub(center).normalize();
|
||||||
|
const proj1 = projectPolygonOntoAxis(p, axis);
|
||||||
|
const proj2 = projectCircleOntoAxis(circle, axis);
|
||||||
|
if (!overlap(proj1, proj2)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
function projectPolygonOntoAxis(p, axis) {
|
||||||
|
let min = Infinity;
|
||||||
|
let max = -Infinity;
|
||||||
|
for (const point of p.points){
|
||||||
|
const dotProduct = point.copy().add(p.center).dot(axis);
|
||||||
|
min = Math.min(min, dotProduct);
|
||||||
|
max = Math.max(max, dotProduct);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
min,
|
||||||
|
max
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function projectCircleOntoAxis(c, axis) {
|
||||||
|
const dot = new Vector(c.center).dot(axis);
|
||||||
|
const min = dot - c.radius;
|
||||||
|
const max = dot + c.radius;
|
||||||
|
return {
|
||||||
|
min,
|
||||||
|
max
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function overlap(proj1, proj2) {
|
||||||
|
return proj1.min <= proj2.max && proj1.max >= proj2.min;
|
||||||
|
}
|
||||||
class SplineSegment {
|
class SplineSegment {
|
||||||
points;
|
points;
|
||||||
length;
|
length;
|
||||||
@ -1594,10 +1597,11 @@ init({
|
|||||||
}, true, (ctx)=>{
|
}, true, (ctx)=>{
|
||||||
ctx.imageSmoothingEnabled = false;
|
ctx.imageSmoothingEnabled = false;
|
||||||
});
|
});
|
||||||
|
doodler.minScale = .1;
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.src = "./pixel fire.gif";
|
img.src = "./pixel fire.gif";
|
||||||
const p = new Vector();
|
const p = new Vector(500, 500);
|
||||||
const gif = new GIFAnimation("./fire-joypixels.gif", p, .5);
|
new GIFAnimation("./fire-joypixels.gif", p, .5);
|
||||||
const spline = new SplineSegment([
|
const spline = new SplineSegment([
|
||||||
new Vector({
|
new Vector({
|
||||||
x: -25,
|
x: -25,
|
||||||
@ -1616,10 +1620,11 @@ const spline = new SplineSegment([
|
|||||||
y: 25
|
y: 25
|
||||||
}).mult(10).add(p)
|
}).mult(10).add(p)
|
||||||
]);
|
]);
|
||||||
|
const poly = Polygon.createPolygon(4);
|
||||||
const poly2 = Polygon.createPolygon(4);
|
const poly2 = Polygon.createPolygon(4);
|
||||||
poly2.center = p.copy().add(100, 100);
|
poly2.center = p.copy().add(100, 100);
|
||||||
doodler.createLayer((c, i, t)=>{
|
doodler.createLayer((c, i, t)=>{
|
||||||
gif.draw(t);
|
c.translate(500, 500);
|
||||||
for(let i = 0; i < c.canvas.width; i += 50){
|
for(let i = 0; i < c.canvas.width; i += 50){
|
||||||
for(let j = 0; j < c.canvas.height; j += 50){
|
for(let j = 0; j < c.canvas.height; j += 50){
|
||||||
doodler.drawSquare(new Vector(i, j), 50, {
|
doodler.drawSquare(new Vector(i, j), 50, {
|
||||||
@ -1627,19 +1632,22 @@ doodler.createLayer((c, i, t)=>{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
poly2.circularHitbox;
|
const intersects = satCollisionCircle(poly, poly2.circularBoundingBox);
|
||||||
const intersects = satCollisionSpline(poly2, spline);
|
|
||||||
const color = intersects ? "red" : "aqua";
|
const color = intersects ? "red" : "aqua";
|
||||||
spline.draw(color);
|
spline.draw(color);
|
||||||
|
poly.draw(color);
|
||||||
poly2.draw(color);
|
poly2.draw(color);
|
||||||
const [gamepad] = navigator.getGamepads();
|
const [gamepad] = navigator.getGamepads();
|
||||||
if (gamepad) {
|
if (gamepad) {
|
||||||
gamepad.axes[0];
|
const leftX = gamepad.axes[0];
|
||||||
gamepad.axes[1];
|
const leftY = gamepad.axes[1];
|
||||||
const rightX = gamepad.axes[2];
|
const rightX = gamepad.axes[2];
|
||||||
const rightY = gamepad.axes[3];
|
const rightY = gamepad.axes[3];
|
||||||
let mMulti = 10;
|
let lMulti = 10;
|
||||||
const mod = new Vector(Math.min(Math.max(rightX - 0.05, 0), rightX + 0.05), Math.min(Math.max(rightY - 0.05, 0), rightY + 0.05));
|
const lMod = new Vector(Math.min(Math.max(leftX - 0.05, 0), leftX + 0.05), Math.min(Math.max(leftY - 0.05, 0), leftY + 0.05));
|
||||||
poly2.center.add(mod.mult(mMulti));
|
poly.center.add(lMod.mult(lMulti));
|
||||||
|
let rMulti = 10;
|
||||||
|
const rMod = new Vector(Math.min(Math.max(rightX - 0.05, 0), rightX + 0.05), Math.min(Math.max(rightY - 0.05, 0), rightY + 0.05));
|
||||||
|
poly2.center.add(rMod.mult(rMulti));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
27
canvas.ts
27
canvas.ts
@ -49,7 +49,7 @@ export class Doodler {
|
|||||||
private layers: layer[] = [];
|
private layers: layer[] = [];
|
||||||
|
|
||||||
protected bg: string;
|
protected bg: string;
|
||||||
private framerate: number;
|
private framerate?: number;
|
||||||
|
|
||||||
get width() {
|
get width() {
|
||||||
return this.ctx.canvas.width;
|
return this.ctx.canvas.width;
|
||||||
@ -77,7 +77,7 @@ export class Doodler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.bg = bg || "white";
|
this.bg = bg || "white";
|
||||||
this.framerate = framerate || 60;
|
this.framerate = framerate;
|
||||||
|
|
||||||
canvas.width = fillScreen ? document.body.clientWidth : width;
|
canvas.width = fillScreen ? document.body.clientWidth : width;
|
||||||
canvas.height = fillScreen ? document.body.clientHeight : height;
|
canvas.height = fillScreen ? document.body.clientHeight : height;
|
||||||
@ -113,11 +113,21 @@ export class Doodler {
|
|||||||
private lastFrameAt = 0;
|
private lastFrameAt = 0;
|
||||||
private startDrawLoop() {
|
private startDrawLoop() {
|
||||||
this.lastFrameAt = Date.now();
|
this.lastFrameAt = Date.now();
|
||||||
this.timer = setInterval(() => this.draw(), 1000 / this.framerate);
|
if (this.framerate) {
|
||||||
|
this.timer = setInterval(
|
||||||
|
() => this.draw(Date.now()),
|
||||||
|
1000 / this.framerate,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const cb = (t: number) => {
|
||||||
|
this.draw(t);
|
||||||
|
requestAnimationFrame(cb);
|
||||||
|
};
|
||||||
|
requestAnimationFrame(cb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected draw() {
|
protected draw(time: number) {
|
||||||
const time = Date.now();
|
|
||||||
const frameTime = time - this.lastFrameAt;
|
const frameTime = time - this.lastFrameAt;
|
||||||
this.ctx.clearRect(0, 0, this.width, this.height);
|
this.ctx.clearRect(0, 0, this.width, this.height);
|
||||||
this.ctx.fillStyle = this.bg;
|
this.ctx.fillStyle = this.bg;
|
||||||
@ -254,6 +264,13 @@ export class Doodler {
|
|||||||
: this.ctx.drawImage(img, at.x, at.y);
|
: this.ctx.drawImage(img, at.x, at.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description This method is VERY expensive and should be used sparingly - O(n^2) where n is weight. Beyond that, it doesn't work with transparency correctly since the image is overlaid multiple times in drawing and the resulting transparency is dependent on the weight provided
|
||||||
|
*
|
||||||
|
* @param img
|
||||||
|
* @param at
|
||||||
|
* @param style
|
||||||
|
*/
|
||||||
drawImageWithOutline(img: HTMLImageElement, at: Vector, style?: IStyle): void;
|
drawImageWithOutline(img: HTMLImageElement, at: Vector, style?: IStyle): void;
|
||||||
drawImageWithOutline(
|
drawImageWithOutline(
|
||||||
img: HTMLImageElement,
|
img: HTMLImageElement,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { Polygon } from "../geometry/polygon.ts";
|
import { Polygon } from "../geometry/polygon.ts";
|
||||||
import { SplineSegment } from "../geometry/spline.ts";
|
import { SplineSegment } from "../geometry/spline.ts";
|
||||||
import { Vector } from "../geometry/vector.ts";
|
import { Vector } from "../geometry/vector.ts";
|
||||||
|
import { axisAlignedBoundingBox } from "./aa.ts";
|
||||||
import { CircleLike } from "./circular.ts";
|
import { CircleLike } from "./circular.ts";
|
||||||
|
|
||||||
export function satCollisionSpline(p: Polygon, spline: SplineSegment): boolean {
|
export function satCollisionSpline(p: Polygon, spline: SplineSegment): boolean {
|
||||||
@ -48,13 +49,25 @@ export function satCollisionCircle(p: Polygon, circle: CircleLike): boolean {
|
|||||||
}
|
}
|
||||||
const center = new Vector(circle.center);
|
const center = new Vector(circle.center);
|
||||||
const nearest = p.getNearestPoint(center);
|
const nearest = p.getNearestPoint(center);
|
||||||
const axis = nearest.copy().normal(center).normalize();
|
const axis = nearest.copy().sub(center).normalize();
|
||||||
const proj1 = projectPolygonOntoAxis(p, axis);
|
const proj1 = projectPolygonOntoAxis(p, axis);
|
||||||
const proj2 = projectCircleOntoAxis(circle, axis);
|
const proj2 = projectCircleOntoAxis(circle, axis);
|
||||||
|
|
||||||
if (!overlap(proj1, proj2)) return false;
|
if (!overlap(proj1, proj2)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
export function satCollisionAABBCircle(
|
||||||
|
aabb: axisAlignedBoundingBox,
|
||||||
|
circle: CircleLike,
|
||||||
|
): boolean {
|
||||||
|
const p = new Polygon([
|
||||||
|
{ x: aabb.x, y: aabb.y },
|
||||||
|
{ x: aabb.x + aabb.w, y: aabb.y },
|
||||||
|
{ x: aabb.x + aabb.w, y: aabb.y + aabb.h },
|
||||||
|
{ x: aabb.x, y: aabb.y + aabb.h },
|
||||||
|
]);
|
||||||
|
return satCollisionCircle(p, circle);
|
||||||
|
}
|
||||||
|
|
||||||
function segmentIntersectsPolygon(
|
function segmentIntersectsPolygon(
|
||||||
p: Polygon,
|
p: Polygon,
|
||||||
|
@ -33,7 +33,14 @@ export class Polygon {
|
|||||||
return center;
|
return center;
|
||||||
}
|
}
|
||||||
|
|
||||||
get circularHitbox(): CircleLike {
|
_circularBoundingBox?: CircleLike;
|
||||||
|
|
||||||
|
get circularBoundingBox(): CircleLike {
|
||||||
|
this._circularBoundingBox = this.calculateCircularBoundingBox();
|
||||||
|
return this._circularBoundingBox;
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculateCircularBoundingBox() {
|
||||||
let greatestDistance = 0;
|
let greatestDistance = 0;
|
||||||
for (const p of this.points) {
|
for (const p of this.points) {
|
||||||
greatestDistance = Math.max(
|
greatestDistance = Math.max(
|
||||||
@ -50,13 +57,11 @@ export class Polygon {
|
|||||||
|
|
||||||
_aabb?: axisAlignedBoundingBox;
|
_aabb?: axisAlignedBoundingBox;
|
||||||
get AABB(): axisAlignedBoundingBox {
|
get AABB(): axisAlignedBoundingBox {
|
||||||
if (!this._aabb) {
|
this._aabb = this.recalculateAABB();
|
||||||
this._aabb = this.recalculateAABB();
|
|
||||||
}
|
|
||||||
return this._aabb;
|
return this._aabb;
|
||||||
}
|
}
|
||||||
|
|
||||||
recalculateAABB(): axisAlignedBoundingBox {
|
private recalculateAABB(): axisAlignedBoundingBox {
|
||||||
let smallestX, biggestX, smallestY, biggestY;
|
let smallestX, biggestX, smallestY, biggestY;
|
||||||
smallestX =
|
smallestX =
|
||||||
smallestY =
|
smallestY =
|
||||||
@ -74,8 +79,8 @@ export class Polygon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: smallestX,
|
x: smallestX + this.center.x,
|
||||||
y: smallestY,
|
y: smallestY + this.center.y,
|
||||||
w: biggestX - smallestX,
|
w: biggestX - smallestX,
|
||||||
h: biggestY - smallestY,
|
h: biggestY - smallestY,
|
||||||
};
|
};
|
||||||
@ -118,6 +123,6 @@ export class Polygon {
|
|||||||
for (const point of this.points) {
|
for (const point of this.points) {
|
||||||
if (p.dist(point) < p.dist(nearest)) nearest = point;
|
if (p.dist(point) < p.dist(nearest)) nearest = point;
|
||||||
}
|
}
|
||||||
return nearest;
|
return nearest.copy().add(this.center);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<title>Doodler</title>
|
<title>Doodler</title>
|
||||||
<style>
|
<style>
|
||||||
* {
|
* {
|
||||||
image-rendering: pixelated;
|
/* image-rendering: pixelated; */
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
51
main.ts
51
main.ts
@ -7,7 +7,11 @@ import { handleGIF } from "./processing/gif.ts";
|
|||||||
import { ZoomableDoodler } from "./zoomableCanvas.ts";
|
import { ZoomableDoodler } from "./zoomableCanvas.ts";
|
||||||
import { axisAlignedCollision, axisAlignedContains } from "./collision/aa.ts";
|
import { axisAlignedCollision, axisAlignedContains } from "./collision/aa.ts";
|
||||||
import { circularCollision } from "./collision/circular.ts";
|
import { circularCollision } from "./collision/circular.ts";
|
||||||
import { satCollisionSpline } from "./collision/sat.ts";
|
import {
|
||||||
|
satCollisionAABBCircle,
|
||||||
|
satCollisionCircle,
|
||||||
|
satCollisionSpline,
|
||||||
|
} from "./collision/sat.ts";
|
||||||
import { Polygon } from "./geometry/polygon.ts";
|
import { Polygon } from "./geometry/polygon.ts";
|
||||||
import { SplineSegment } from "./geometry/spline.ts";
|
import { SplineSegment } from "./geometry/spline.ts";
|
||||||
// import { ZoomableDoodler } from "./zoomableCanvas.ts";
|
// import { ZoomableDoodler } from "./zoomableCanvas.ts";
|
||||||
@ -22,10 +26,11 @@ initializeDoodler(
|
|||||||
true,
|
true,
|
||||||
(ctx) => {
|
(ctx) => {
|
||||||
ctx.imageSmoothingEnabled = false;
|
ctx.imageSmoothingEnabled = false;
|
||||||
// ctx.translate(1200, 600);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
(doodler as ZoomableDoodler).minScale = .1;
|
||||||
|
|
||||||
// const movingVector = new Vector(100, 300);
|
// const movingVector = new Vector(100, 300);
|
||||||
// let angleMultiplier = 0;
|
// let angleMultiplier = 0;
|
||||||
// const v = new Vector(30, 30);
|
// const v = new Vector(30, 30);
|
||||||
@ -33,7 +38,7 @@ initializeDoodler(
|
|||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.src = "./pixel fire.gif";
|
img.src = "./pixel fire.gif";
|
||||||
|
|
||||||
const p = new Vector();
|
const p = new Vector(500, 500);
|
||||||
const gif = new GIFAnimation("./fire-joypixels.gif", p, .5);
|
const gif = new GIFAnimation("./fire-joypixels.gif", p, .5);
|
||||||
|
|
||||||
const spline = new SplineSegment([
|
const spline = new SplineSegment([
|
||||||
@ -44,25 +49,26 @@ const spline = new SplineSegment([
|
|||||||
]);
|
]);
|
||||||
// poly.center = p.copy();
|
// poly.center = p.copy();
|
||||||
|
|
||||||
|
const poly = Polygon.createPolygon(4);
|
||||||
const poly2 = Polygon.createPolygon(4);
|
const poly2 = Polygon.createPolygon(4);
|
||||||
|
|
||||||
poly2.center = p.copy().add(100, 100);
|
poly2.center = p.copy().add(100, 100);
|
||||||
// poly.center.add(p);
|
// poly.center.add(p);
|
||||||
|
|
||||||
doodler.createLayer((c, i, t) => {
|
doodler.createLayer((c, i, t) => {
|
||||||
gif.draw(t);
|
// gif.draw(t);
|
||||||
// c.translate(1200, 600);
|
c.translate(500, 500);
|
||||||
for (let i = 0; i < c.canvas.width; i += 50) {
|
for (let i = 0; i < c.canvas.width; i += 50) {
|
||||||
for (let j = 0; j < c.canvas.height; j += 50) {
|
for (let j = 0; j < c.canvas.height; j += 50) {
|
||||||
doodler.drawSquare(new Vector(i, j), 50, { color: "#00000010" });
|
doodler.drawSquare(new Vector(i, j), 50, { color: "#00000010" });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const cir = poly2.circularHitbox;
|
// const cir = poly2.circularHitbox;
|
||||||
// const t = spline.getPointsWithinRadius(
|
// const t = spline.getPointsWithinRadius(
|
||||||
// new Vector(cir.center),
|
// new Vector(cir.center),
|
||||||
// cir.radius,
|
// cir.radius,
|
||||||
// ).map((t) => t[0]);
|
// ).map((t) => t[0]);
|
||||||
const intersects = satCollisionSpline(poly2, spline);
|
const intersects = satCollisionCircle(poly, poly2.circularBoundingBox);
|
||||||
const color = intersects ? "red" : "aqua";
|
const color = intersects ? "red" : "aqua";
|
||||||
|
|
||||||
// const point = spline.getPointAtT(t || 0);
|
// const point = spline.getPointAtT(t || 0);
|
||||||
@ -80,6 +86,7 @@ doodler.createLayer((c, i, t) => {
|
|||||||
|
|
||||||
spline.draw(color);
|
spline.draw(color);
|
||||||
|
|
||||||
|
poly.draw(color);
|
||||||
poly2.draw(color);
|
poly2.draw(color);
|
||||||
|
|
||||||
// poly2.center.add(Vector.random2D());
|
// poly2.center.add(Vector.random2D());
|
||||||
@ -112,21 +119,37 @@ doodler.createLayer((c, i, t) => {
|
|||||||
// Math.min(Math.max(leftY - deadzone, 0), leftY + deadzone),
|
// Math.min(Math.max(leftY - deadzone, 0), leftY + deadzone),
|
||||||
// ).mult(10),
|
// ).mult(10),
|
||||||
// );
|
// );
|
||||||
let mMulti = 10;
|
let lMulti = 10;
|
||||||
const mod = new Vector(
|
const lMod = new Vector(
|
||||||
|
Math.min(Math.max(leftX - deadzone, 0), leftX + deadzone),
|
||||||
|
Math.min(Math.max(leftY - deadzone, 0), leftY + deadzone),
|
||||||
|
);
|
||||||
|
// let future = new Vector(cir.center).add(mod.copy().mult(lMulti--));
|
||||||
|
// while (spline.intersectsCircle(future, cir.radius)) {
|
||||||
|
// // if (lMulti === 0) {
|
||||||
|
// // lMulti = 1;
|
||||||
|
// // break;
|
||||||
|
// // }
|
||||||
|
// future = new Vector(cir.center).add(mod.copy().mult(lMulti--));
|
||||||
|
// }
|
||||||
|
poly.center.add(
|
||||||
|
lMod.mult(lMulti),
|
||||||
|
);
|
||||||
|
let rMulti = 10;
|
||||||
|
const rMod = new Vector(
|
||||||
Math.min(Math.max(rightX - deadzone, 0), rightX + deadzone),
|
Math.min(Math.max(rightX - deadzone, 0), rightX + deadzone),
|
||||||
Math.min(Math.max(rightY - deadzone, 0), rightY + deadzone),
|
Math.min(Math.max(rightY - deadzone, 0), rightY + deadzone),
|
||||||
);
|
);
|
||||||
// let future = new Vector(cir.center).add(mod.copy().mult(mMulti--));
|
// let future = new Vector(cir.center).add(mod.copy().mult(rMulti--));
|
||||||
// while (spline.intersectsCircle(future, cir.radius)) {
|
// while (spline.intersectsCircle(future, cir.radius)) {
|
||||||
// // if (mMulti === 0) {
|
// // if (rMulti === 0) {
|
||||||
// // mMulti = 1;
|
// // rMulti = 1;
|
||||||
// // break;
|
// // break;
|
||||||
// // }
|
// // }
|
||||||
// future = new Vector(cir.center).add(mod.copy().mult(mMulti--));
|
// future = new Vector(cir.center).add(mod.copy().mult(rMulti--));
|
||||||
// }
|
// }
|
||||||
poly2.center.add(
|
poly2.center.add(
|
||||||
mod.mult(mMulti),
|
rMod.mult(rMulti),
|
||||||
);
|
);
|
||||||
|
|
||||||
// (doodler as ZoomableDoodler).moveOrigin({ x: -rigthX * 5, y: -rigthY * 5 });
|
// (doodler as ZoomableDoodler).moveOrigin({ x: -rigthX * 5, y: -rigthY * 5 });
|
||||||
|
@ -28,6 +28,7 @@ export class ZoomableDoodler extends Doodler {
|
|||||||
scaleAround: Point = { x: 0, y: 0 };
|
scaleAround: Point = { x: 0, y: 0 };
|
||||||
|
|
||||||
maxScale = 4;
|
maxScale = 4;
|
||||||
|
minScale = 1;
|
||||||
|
|
||||||
constructor(options: DoodlerOptions, postInit?: postInit) {
|
constructor(options: DoodlerOptions, postInit?: postInit) {
|
||||||
super(options, postInit);
|
super(options, postInit);
|
||||||
@ -190,7 +191,10 @@ export class ZoomableDoodler extends Doodler {
|
|||||||
}, scaleBy);
|
}, scaleBy);
|
||||||
}
|
}
|
||||||
scaleAt(p: Point, scaleBy: number) {
|
scaleAt(p: Point, scaleBy: number) {
|
||||||
this.scale = Math.min(Math.max(this.scale * scaleBy, 1), this.maxScale);
|
this.scale = Math.min(
|
||||||
|
Math.max(this.scale * scaleBy, this.minScale),
|
||||||
|
this.maxScale,
|
||||||
|
);
|
||||||
this.origin.x = p.x - (p.x - this.origin.x) * scaleBy;
|
this.origin.x = p.x - (p.x - this.origin.x) * scaleBy;
|
||||||
this.origin.y = p.y - (p.y - this.origin.y) * scaleBy;
|
this.origin.y = p.y - (p.y - this.origin.y) * scaleBy;
|
||||||
this.constrainOrigin();
|
this.constrainOrigin();
|
||||||
@ -228,7 +232,7 @@ export class ZoomableDoodler extends Doodler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
draw() {
|
draw(time: number) {
|
||||||
this.ctx.setTransform(
|
this.ctx.setTransform(
|
||||||
this.scale,
|
this.scale,
|
||||||
0,
|
0,
|
||||||
@ -240,7 +244,7 @@ export class ZoomableDoodler extends Doodler {
|
|||||||
this.animateZoom();
|
this.animateZoom();
|
||||||
this.ctx.fillStyle = this.bg;
|
this.ctx.fillStyle = this.bg;
|
||||||
this.ctx.fillRect(0, 0, this.width / this.scale, this.height / this.scale);
|
this.ctx.fillRect(0, 0, this.width / this.scale, this.height / this.scale);
|
||||||
super.draw();
|
super.draw(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTouchOffset(p: Point) {
|
getTouchOffset(p: Point) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user