UNPKG

@vicimpa/lib-vec2

Version:

A comprehensive TypeScript library for 2D vector manipulation, compatible with CanvasRenderingContext2D.

422 lines 11.1 kB
'use strict';function vec2(x, y) { const vec = new Vec2(); if (x === undefined) return vec; if (typeof x === 'object') return vec.set(x); return vec.set(x, y ?? x); } class Vec2 { get point() { return { ...this }; } get tuple() { return [...this]; } get size() { return { width: this.x, height: this.y }; } get p() { return this.point; } get t() { return this.tuple; } get s() { return this.size; } *[Symbol.iterator]() { yield this.x; yield this.y; } toString() { return `Vec2 { x: ${this.x}, y: ${this.y} }`; } constructor(x, y) { this.x = 0; this.y = 0; if (x === undefined) return; if (typeof x === 'object') return this.set(x); this.set(x, y ?? x); } equal(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return this.x === x && this.y === (y ?? x); } set(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return (this.x = x, this.y = y ?? x, this); } toObject(o) { o.x = this.x; o.y = this.y; return this; } toObjectSize(o) { o.width = this.x; o.height = this.y; return this; } toTuple(o) { o[0] = this.x; o[1] = this.y; return this; } clone() { return new Vec2(this); } min() { return Math.min(this.x, this.y); } max() { return Math.max(this.x, this.y); } angle() { return Math.atan2(this.y, this.x); } length() { return Math.hypot(this.x, this.y); } distance(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return Math.hypot(this.x - x, this.y - (y ?? x)); } dot(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return this.x * x + this.y * (y ?? x); } scalar(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return this.dot(x, y ?? x) / Math.hypot(x, y ?? x); } plus(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return (this.x += x, this.y += y ?? x, this); } minus(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return (this.x -= x, this.y -= y ?? x, this); } times(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return (this.x *= x, this.y *= y ?? x, this); } div(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return (this.x /= x, this.y /= y ?? x, this); } rem(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return (this.x %= x, this.y %= y ?? x, this); } pow(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return (this.x **= x, this.y **= y ?? x, this); } abs() { this.x = Math.abs(this.x); this.y = Math.abs(this.y); return this; } sign() { this.x = Math.sign(this.x); this.y = Math.sign(this.y); return this; } round() { this.x = Math.round(this.x); this.y = Math.round(this.y); return this; } ceil() { this.x = Math.ceil(this.x); this.y = Math.ceil(this.y); return this; } floor() { this.x = Math.floor(this.x); this.y = Math.floor(this.y); return this; } normalize() { return this.equal(0) ? this : this.div(this.length()); } inverse() { return this.set(this.y, this.x); } clampMin(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); this.x = Math.max(this.x, x); this.y = Math.max(this.y, y ?? x); return this; } clampMax(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); this.x = Math.min(this.x, x); this.y = Math.min(this.y, y ?? x); return this; } clamp(...args) { if (args.length === 2) this.clampMin(args[0]).clampMax(args[1]); else if (args.length === 4) this.clampMin(args[0], args[1]).clampMax(args[2], args[3]); else throw new Error('Invalid arguments'); return this; } cplus(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return this.clone().plus(x, y ?? x); } cminus(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return this.clone().minus(x, y ?? x); } ctimes(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return this.clone().times(x, y ?? x); } cdiv(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return this.clone().div(x, y ?? x); } crem(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return this.clone().rem(x, y ?? x); } cpow(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return this.clone().pow(x, y ?? x); } cabs() { return this.clone().abs(); } csign() { return this.clone().sign(); } cround() { return this.clone().round(); } cceil() { return this.clone().ceil(); } cfloor() { return this.clone().floor(); } cnormalize() { return this.clone().normalize(); } cinverse() { return this.clone().inverse(); } cclampMin(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return this.clone().clampMin(x, y ?? x); } cclampMax(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return this.clone().clampMax(x, y ?? x); } cclamp(...args) { return this.clone().clamp(...args); } static fromAngle(angle, vec = new this()) { return vec.set(Math.sin(angle), Math.cos(angle)); } static fromRandom(vec = new this()) { return vec.set(Math.random(), Math.random()); } static fromSrandom(vec = new this()) { return this.fromRandom(vec).times(2).minus(1); } static fromSize(size, vec = new this()) { return vec.set(size.width, size.height); } static fromDeltaXY(page, vec = new this()) { return vec.set(page.deltaX, page.deltaY); } static fromPageXY(page, vec = new this()) { return vec.set(page.pageX, page.pageY); } static fromOffsetXY(offset, vec = new this()) { return vec.set(offset.offsetX, offset.offsetY); } static fromInnerSize(offsetSize, vec = new this()) { return vec.set(offsetSize.innerWidth, offsetSize.innerHeight); } static fromOffsetSize(offsetSize, vec = new this()) { return vec.set(offsetSize.offsetWidth, offsetSize.offsetHeight); } static fromSvgLength(x, y, vec = new this()) { return vec.set(x.baseVal.value, y.baseVal.value); } } class Vec2Map { constructor() { this._data = new Map(); this._keys = {}; this._vectors = {}; } get size() { return this._data.size; } has(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return this._data.has(this._keys[x]?.[y ?? x]); } get(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return this._data.get(this._keys[x]?.[y ?? x]); } set(x, y, z) { if (z === undefined) (z = y, y = undefined); if (typeof x === 'object') (y = x.y, x = x.x); y = (y ?? x); const key = (this._keys[x] ?? (this._keys[x] = {}))[y] ?? (this._keys[x][y] = Symbol('')); this._vectors[key] = { x, y }; this._data.set(key, z); return this; } delete(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return !!this._data.delete(this._keys[x]?.[y ?? x]); } clear() { this._keys = {}; this._vectors = {}; this._data.clear(); return this; } forEach(callback) { this._data.forEach((value, key) => { callback(value, vec2(this._vectors[key])); }); } *[Symbol.iterator]() { for (const item of this._data) { yield [vec2(this._vectors[item[0]]), item[1]]; } } } class Vec2Set { constructor() { this._data = new Set(); this._keys = {}; this._vectors = {}; } get size() { return this._data.size; } has(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return this._data.has(this._keys[x]?.[y ?? x]); } add(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); y = y ?? x; const key = (this._keys[x] ?? (this._keys[x] = {}))[y] ?? (this._keys[x][y] = Symbol('')); this._vectors[key] = { x, y }; this._data.add(key); return this; } delete(x, y) { if (typeof x === 'object') (y = x.y, x = x.x); return !!this._data.delete(this._keys[x]?.[y ?? x]); } clear() { this._keys = {}; this._vectors = {}; this._data.clear(); return this; } forEach(callback) { this._data.forEach((value) => { callback(vec2(this._vectors[value])); }); } *[Symbol.iterator]() { for (const item of this._data) { yield vec2(this._vectors[item]); } } }const methods = [ 'clearRect', 'fillRect', 'strokeRect', 'arc', 'arcTo', 'bezierCurveTo', 'ellipse', 'lineTo', 'moveTo', 'quadraticCurveTo', 'rect', 'roundRect', 'drawImage', 'createImageData', 'getImageData', 'putImageData', 'isPointInPath', 'isPointInStroke', 'createConicGradient', 'createLinearGradient', 'createRadialGradient', 'fillText', 'strokeText', 'scale', 'setTransform', 'transform', 'translate' ]; const mapped = (args) => { for (let i = 0; i < args.length; i++) { const element = args[i]; if (element instanceof Vec2) { args.splice(i, 1, ...element); i += 1; } } return args; }; const patch = (func) => (function (...args) { return func.apply(this, mapped(args)); }); if (typeof CanvasRenderingContext2D !== 'undefined') { const { prototype } = CanvasRenderingContext2D; for (const key of methods) { if (key in prototype) Object.assign(prototype, { [key]: patch(prototype[key]) }); } } if (typeof Path2D !== 'undefined') { const { prototype } = Path2D; for (const key of methods) { if (key in prototype) { Object.assign(prototype, { [key]: patch(prototype[key]) }); } } }exports.Vec2=Vec2;exports.Vec2Map=Vec2Map;exports.Vec2Set=Vec2Set;exports.vec2=vec2;