Fixes circular SAT
This commit is contained in:
parent
e70787260a
commit
da77aa10bb
150
bundle.js
150
bundle.js
@ -707,7 +707,7 @@ class Doodler {
|
||||
document.body.append(canvas);
|
||||
}
|
||||
this.bg = bg || "white";
|
||||
this.framerate = framerate || 60;
|
||||
this.framerate = framerate;
|
||||
canvas.width = fillScreen ? document.body.clientWidth : width;
|
||||
canvas.height = fillScreen ? document.body.clientHeight : height;
|
||||
if (fillScreen) {
|
||||
@ -735,10 +735,17 @@ class Doodler {
|
||||
lastFrameAt = 0;
|
||||
startDrawLoop() {
|
||||
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() {
|
||||
const time = Date.now();
|
||||
}
|
||||
draw(time) {
|
||||
const frameTime = time - this.lastFrameAt;
|
||||
this.ctx.clearRect(0, 0, this.width, this.height);
|
||||
this.ctx.fillStyle = this.bg;
|
||||
@ -1033,6 +1040,7 @@ class ZoomableDoodler extends Doodler {
|
||||
y: 0
|
||||
};
|
||||
maxScale = 4;
|
||||
minScale = 1;
|
||||
constructor(options, postInit){
|
||||
super(options, postInit);
|
||||
this._canvas.addEventListener("wheel", (e)=>{
|
||||
@ -1180,7 +1188,7 @@ class ZoomableDoodler extends Doodler {
|
||||
}, 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.y = p.y - (p.y - this.origin.y) * scaleBy;
|
||||
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.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.animateZoom();
|
||||
this.ctx.fillStyle = this.bg;
|
||||
this.ctx.fillRect(0, 0, this.width / this.scale, this.height / this.scale);
|
||||
super.draw();
|
||||
super.draw(time);
|
||||
}
|
||||
getTouchOffset(p) {
|
||||
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.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 {
|
||||
points;
|
||||
center;
|
||||
@ -1342,7 +1302,12 @@ class Polygon {
|
||||
center.div(this.points.length);
|
||||
return center;
|
||||
}
|
||||
get circularHitbox() {
|
||||
_circularBoundingBox;
|
||||
get circularBoundingBox() {
|
||||
this._circularBoundingBox = this.calculateCircularBoundingBox();
|
||||
return this._circularBoundingBox;
|
||||
}
|
||||
calculateCircularBoundingBox() {
|
||||
let greatestDistance = 0;
|
||||
for (const p of this.points){
|
||||
greatestDistance = Math.max(p.copy().add(this.center).dist(this.center), greatestDistance);
|
||||
@ -1354,9 +1319,7 @@ class Polygon {
|
||||
}
|
||||
_aabb;
|
||||
get AABB() {
|
||||
if (!this._aabb) {
|
||||
this._aabb = this.recalculateAABB();
|
||||
}
|
||||
return this._aabb;
|
||||
}
|
||||
recalculateAABB() {
|
||||
@ -1371,8 +1334,8 @@ class Polygon {
|
||||
biggestY = Math.max(temp.y, biggestY);
|
||||
}
|
||||
return {
|
||||
x: smallestX,
|
||||
y: smallestY,
|
||||
x: smallestX + this.center.x,
|
||||
y: smallestY + this.center.y,
|
||||
w: biggestX - smallestX,
|
||||
h: biggestY - smallestY
|
||||
};
|
||||
@ -1407,9 +1370,49 @@ class Polygon {
|
||||
for (const point of this.points){
|
||||
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 {
|
||||
points;
|
||||
length;
|
||||
@ -1594,10 +1597,11 @@ init({
|
||||
}, true, (ctx)=>{
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
});
|
||||
doodler.minScale = .1;
|
||||
const img = new Image();
|
||||
img.src = "./pixel fire.gif";
|
||||
const p = new Vector();
|
||||
const gif = new GIFAnimation("./fire-joypixels.gif", p, .5);
|
||||
const p = new Vector(500, 500);
|
||||
new GIFAnimation("./fire-joypixels.gif", p, .5);
|
||||
const spline = new SplineSegment([
|
||||
new Vector({
|
||||
x: -25,
|
||||
@ -1616,10 +1620,11 @@ const spline = new SplineSegment([
|
||||
y: 25
|
||||
}).mult(10).add(p)
|
||||
]);
|
||||
const poly = Polygon.createPolygon(4);
|
||||
const poly2 = Polygon.createPolygon(4);
|
||||
poly2.center = p.copy().add(100, 100);
|
||||
doodler.createLayer((c, i, t)=>{
|
||||
gif.draw(t);
|
||||
c.translate(500, 500);
|
||||
for(let i = 0; i < c.canvas.width; i += 50){
|
||||
for(let j = 0; j < c.canvas.height; j += 50){
|
||||
doodler.drawSquare(new Vector(i, j), 50, {
|
||||
@ -1627,19 +1632,22 @@ doodler.createLayer((c, i, t)=>{
|
||||
});
|
||||
}
|
||||
}
|
||||
poly2.circularHitbox;
|
||||
const intersects = satCollisionSpline(poly2, spline);
|
||||
const intersects = satCollisionCircle(poly, poly2.circularBoundingBox);
|
||||
const color = intersects ? "red" : "aqua";
|
||||
spline.draw(color);
|
||||
poly.draw(color);
|
||||
poly2.draw(color);
|
||||
const [gamepad] = navigator.getGamepads();
|
||||
if (gamepad) {
|
||||
gamepad.axes[0];
|
||||
gamepad.axes[1];
|
||||
const leftX = gamepad.axes[0];
|
||||
const leftY = gamepad.axes[1];
|
||||
const rightX = gamepad.axes[2];
|
||||
const rightY = gamepad.axes[3];
|
||||
let mMulti = 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));
|
||||
poly2.center.add(mod.mult(mMulti));
|
||||
let lMulti = 10;
|
||||
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));
|
||||
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[] = [];
|
||||
|
||||
protected bg: string;
|
||||
private framerate: number;
|
||||
private framerate?: number;
|
||||
|
||||
get width() {
|
||||
return this.ctx.canvas.width;
|
||||
@ -77,7 +77,7 @@ export class Doodler {
|
||||
}
|
||||
|
||||
this.bg = bg || "white";
|
||||
this.framerate = framerate || 60;
|
||||
this.framerate = framerate;
|
||||
|
||||
canvas.width = fillScreen ? document.body.clientWidth : width;
|
||||
canvas.height = fillScreen ? document.body.clientHeight : height;
|
||||
@ -113,11 +113,21 @@ export class Doodler {
|
||||
private lastFrameAt = 0;
|
||||
private startDrawLoop() {
|
||||
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() {
|
||||
const time = Date.now();
|
||||
protected draw(time: number) {
|
||||
const frameTime = time - this.lastFrameAt;
|
||||
this.ctx.clearRect(0, 0, this.width, this.height);
|
||||
this.ctx.fillStyle = this.bg;
|
||||
@ -254,6 +264,13 @@ export class Doodler {
|
||||
: 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,
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Polygon } from "../geometry/polygon.ts";
|
||||
import { SplineSegment } from "../geometry/spline.ts";
|
||||
import { Vector } from "../geometry/vector.ts";
|
||||
import { axisAlignedBoundingBox } from "./aa.ts";
|
||||
import { CircleLike } from "./circular.ts";
|
||||
|
||||
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 nearest = p.getNearestPoint(center);
|
||||
const axis = nearest.copy().normal(center).normalize();
|
||||
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;
|
||||
}
|
||||
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(
|
||||
p: Polygon,
|
||||
|
@ -33,7 +33,14 @@ export class Polygon {
|
||||
return center;
|
||||
}
|
||||
|
||||
get circularHitbox(): CircleLike {
|
||||
_circularBoundingBox?: CircleLike;
|
||||
|
||||
get circularBoundingBox(): CircleLike {
|
||||
this._circularBoundingBox = this.calculateCircularBoundingBox();
|
||||
return this._circularBoundingBox;
|
||||
}
|
||||
|
||||
private calculateCircularBoundingBox() {
|
||||
let greatestDistance = 0;
|
||||
for (const p of this.points) {
|
||||
greatestDistance = Math.max(
|
||||
@ -50,13 +57,11 @@ export class Polygon {
|
||||
|
||||
_aabb?: axisAlignedBoundingBox;
|
||||
get AABB(): axisAlignedBoundingBox {
|
||||
if (!this._aabb) {
|
||||
this._aabb = this.recalculateAABB();
|
||||
}
|
||||
return this._aabb;
|
||||
}
|
||||
|
||||
recalculateAABB(): axisAlignedBoundingBox {
|
||||
private recalculateAABB(): axisAlignedBoundingBox {
|
||||
let smallestX, biggestX, smallestY, biggestY;
|
||||
smallestX =
|
||||
smallestY =
|
||||
@ -74,8 +79,8 @@ export class Polygon {
|
||||
}
|
||||
|
||||
return {
|
||||
x: smallestX,
|
||||
y: smallestY,
|
||||
x: smallestX + this.center.x,
|
||||
y: smallestY + this.center.y,
|
||||
w: biggestX - smallestX,
|
||||
h: biggestY - smallestY,
|
||||
};
|
||||
@ -118,6 +123,6 @@ export class Polygon {
|
||||
for (const point of this.points) {
|
||||
if (p.dist(point) < p.dist(nearest)) nearest = point;
|
||||
}
|
||||
return nearest;
|
||||
return nearest.copy().add(this.center);
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
<title>Doodler</title>
|
||||
<style>
|
||||
* {
|
||||
image-rendering: pixelated;
|
||||
/* image-rendering: pixelated; */
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
51
main.ts
51
main.ts
@ -7,7 +7,11 @@ import { handleGIF } from "./processing/gif.ts";
|
||||
import { ZoomableDoodler } from "./zoomableCanvas.ts";
|
||||
import { axisAlignedCollision, axisAlignedContains } from "./collision/aa.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 { SplineSegment } from "./geometry/spline.ts";
|
||||
// import { ZoomableDoodler } from "./zoomableCanvas.ts";
|
||||
@ -22,10 +26,11 @@ initializeDoodler(
|
||||
true,
|
||||
(ctx) => {
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
// ctx.translate(1200, 600);
|
||||
},
|
||||
);
|
||||
|
||||
(doodler as ZoomableDoodler).minScale = .1;
|
||||
|
||||
// const movingVector = new Vector(100, 300);
|
||||
// let angleMultiplier = 0;
|
||||
// const v = new Vector(30, 30);
|
||||
@ -33,7 +38,7 @@ initializeDoodler(
|
||||
const img = new Image();
|
||||
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 spline = new SplineSegment([
|
||||
@ -44,25 +49,26 @@ const spline = new SplineSegment([
|
||||
]);
|
||||
// poly.center = p.copy();
|
||||
|
||||
const poly = Polygon.createPolygon(4);
|
||||
const poly2 = Polygon.createPolygon(4);
|
||||
|
||||
poly2.center = p.copy().add(100, 100);
|
||||
// poly.center.add(p);
|
||||
|
||||
doodler.createLayer((c, i, t) => {
|
||||
gif.draw(t);
|
||||
// c.translate(1200, 600);
|
||||
// gif.draw(t);
|
||||
c.translate(500, 500);
|
||||
for (let i = 0; i < c.canvas.width; i += 50) {
|
||||
for (let j = 0; j < c.canvas.height; j += 50) {
|
||||
doodler.drawSquare(new Vector(i, j), 50, { color: "#00000010" });
|
||||
}
|
||||
}
|
||||
const cir = poly2.circularHitbox;
|
||||
// const cir = poly2.circularHitbox;
|
||||
// const t = spline.getPointsWithinRadius(
|
||||
// new Vector(cir.center),
|
||||
// cir.radius,
|
||||
// ).map((t) => t[0]);
|
||||
const intersects = satCollisionSpline(poly2, spline);
|
||||
const intersects = satCollisionCircle(poly, poly2.circularBoundingBox);
|
||||
const color = intersects ? "red" : "aqua";
|
||||
|
||||
// const point = spline.getPointAtT(t || 0);
|
||||
@ -80,6 +86,7 @@ doodler.createLayer((c, i, t) => {
|
||||
|
||||
spline.draw(color);
|
||||
|
||||
poly.draw(color);
|
||||
poly2.draw(color);
|
||||
|
||||
// poly2.center.add(Vector.random2D());
|
||||
@ -112,21 +119,37 @@ doodler.createLayer((c, i, t) => {
|
||||
// Math.min(Math.max(leftY - deadzone, 0), leftY + deadzone),
|
||||
// ).mult(10),
|
||||
// );
|
||||
let mMulti = 10;
|
||||
const mod = new Vector(
|
||||
let lMulti = 10;
|
||||
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(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)) {
|
||||
// // if (mMulti === 0) {
|
||||
// // mMulti = 1;
|
||||
// // if (rMulti === 0) {
|
||||
// // rMulti = 1;
|
||||
// // break;
|
||||
// // }
|
||||
// future = new Vector(cir.center).add(mod.copy().mult(mMulti--));
|
||||
// future = new Vector(cir.center).add(mod.copy().mult(rMulti--));
|
||||
// }
|
||||
poly2.center.add(
|
||||
mod.mult(mMulti),
|
||||
rMod.mult(rMulti),
|
||||
);
|
||||
|
||||
// (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 };
|
||||
|
||||
maxScale = 4;
|
||||
minScale = 1;
|
||||
|
||||
constructor(options: DoodlerOptions, postInit?: postInit) {
|
||||
super(options, postInit);
|
||||
@ -190,7 +191,10 @@ export class ZoomableDoodler extends Doodler {
|
||||
}, scaleBy);
|
||||
}
|
||||
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.y = p.y - (p.y - this.origin.y) * scaleBy;
|
||||
this.constrainOrigin();
|
||||
@ -228,7 +232,7 @@ export class ZoomableDoodler extends Doodler {
|
||||
);
|
||||
}
|
||||
|
||||
draw() {
|
||||
draw(time: number) {
|
||||
this.ctx.setTransform(
|
||||
this.scale,
|
||||
0,
|
||||
@ -240,7 +244,7 @@ export class ZoomableDoodler extends Doodler {
|
||||
this.animateZoom();
|
||||
this.ctx.fillStyle = this.bg;
|
||||
this.ctx.fillRect(0, 0, this.width / this.scale, this.height / this.scale);
|
||||
super.draw();
|
||||
super.draw(time);
|
||||
}
|
||||
|
||||
getTouchOffset(p: Point) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user