UNPKG

@bitblit/ratchet-common

Version:

Common tools for general use

290 lines 11.6 kB
import { BooleanRatchet } from "../lang/boolean-ratchet.js"; import { ErrorRatchet } from "../lang/error-ratchet.js"; import { RequireRatchet } from "../lang/require-ratchet.js"; import { MatrixFactory } from "./matrix-factory.js"; import { Plane2dType } from "./plane-2d-type.js"; export class Ratchet2d { constructor() { } static translateToOriginVector(points) { let rval = null; if (Ratchet2d.validPoints(points)) { rval = { x: null, y: null, }; points.forEach((p) => { rval.x = rval.x === null || p.x < rval.x ? p.x : rval.x; rval.y = rval.y === null || p.y < rval.y ? p.y : rval.y; }); rval.x *= -1; rval.y *= -1; } return rval; } static minimalContainingPlane(points, type = Plane2dType.BottomLeft) { let rval = null; if (Ratchet2d.validPoints(points)) { rval = { width: 0, height: 0, type: type }; points.forEach((p) => { rval.width = Math.max(rval.width, p.x + 1); rval.height = Math.max(rval.height, p.y + 1); }); } return rval; } static scaleVector(src, dst) { let rval = null; if (Ratchet2d.validPlane(src) && Ratchet2d.validPlane(dst)) { rval = { x: dst.width / src.width, y: dst.height / src.height, }; } return rval; } static samePoint(p1, p2) { return !!p1 && !!p2 && p1.x === p2.x && p1.y === p2.y; } static atLeastOneDimensionShared(p1, p2) { return !!p1 && !!p2 && (p1.x === p2.x || p1.y === p2.y); } static validPlane(plane) { return !!plane && !!plane.height && !!plane.width && !!plane.type; } static validLine(line) { return !!line && Ratchet2d.validPoint(line.p1) && Ratchet2d.validPoint(line.p2) && !Ratchet2d.samePoint(line.p1, line.p2); } static validLines(lines) { return !!lines && BooleanRatchet.allTrue(lines.map((l) => Ratchet2d.validLine(l))); } static validPolyLine(pline) { return !!pline && pline.pts?.length > 1 && BooleanRatchet.allTrue(pline.pts.map((p) => Ratchet2d.validPoint(p))); } static polyLineToLines(pline) { let rval = null; if (Ratchet2d.validPolyLine(pline)) { rval = []; for (let i = 1; i < pline.pts.length; i++) { rval.push({ p1: pline.pts[i - 1], p2: pline.pts[i] }); } } return rval; } static lastPointOnPolyLine(polyLine) { return Ratchet2d.validPolyLine(polyLine) ? polyLine.pts[polyLine.pts.length - 1] : null; } static lineToPolyLine(line) { return Ratchet2d.validLine(line) ? { pts: [line.p1, line.p2] } : null; } static linesToPolyLines(lines) { let rval = null; if (Ratchet2d.validLines(lines)) { rval = []; let currentPLine = Ratchet2d.lineToPolyLine(lines[0]); for (let i = 1; i < lines.length; i++) { if (Ratchet2d.samePoint(Ratchet2d.lastPointOnPolyLine(currentPLine), lines[i].p1)) { currentPLine.pts.push(lines[i].p2); } else { rval.push(currentPLine); currentPLine = Ratchet2d.lineToPolyLine(lines[i]); } } rval.push(currentPLine); } return rval; } static validPoint(pt) { return !!pt && pt.x != null && pt.x != undefined && pt.y !== null && pt.y !== undefined; } static validPoints(pt) { return (!!pt && BooleanRatchet.allTrue(pt.map((p) => Ratchet2d.validPoint(p)), false)); } static planeContainsPoint(pt, plane) { return Ratchet2d.validPlane(plane) && Ratchet2d.validPoint(pt) && pt.x >= 0 && pt.x < plane.width && pt.y >= 0 && pt.y < plane.height; } static planeContainsPoints(pt, plane) { return pt && pt.map((p) => Ratchet2d.planeContainsPoint(p, plane)).reduce((a, i) => a && i, true); } static linesToPoints(lines) { let rval = null; if (Ratchet2d.validLines(lines)) { rval = lines.map((l) => [l.p1, l.p2]).flat(); } return rval; } static pointsToLines(points) { let rval = null; if (Ratchet2d.validPoints(points)) { if (points.length % 2 == 0) { rval = []; for (let i = 0; i < points.length; i += 2) { rval.push({ p1: points[i], p2: points[i + 1] }); } } else { throw new Error('Cannot convert non-even array of points into lines'); } } return rval; } static rotateRightAboutCartesianOrigin90Degrees(points, times = 1) { let rval = null; let translate = { x: 0, y: 0 }; if (Ratchet2d.validPoints(points)) { rval = Object.assign([], points); translate = Ratchet2d.translateToOriginVector(rval); rval = Ratchet2d.translate(rval, translate); let plane = Ratchet2d.minimalContainingPlane(rval); rval = Ratchet2d.xToY(rval); plane = { width: plane.height, height: plane.width, type: Plane2dType.BottomLeft }; translate = { x: translate.y * -1, y: translate.x * -1 }; rval = Ratchet2d.mirrorPointsOnPlane(rval, plane, true, false); rval = Ratchet2d.translate(rval, translate); } const timesToRotate = times > 4 ? times % 4 : times; if (timesToRotate > 1) { rval = Ratchet2d.rotateRightAboutCartesianOrigin90Degrees(rval, timesToRotate - 1); } return rval; } static planeContainsLines(lines, plane) { return lines && Ratchet2d.planeContainsPoints(lines.map((l) => [l.p1, l.p2]).flat(), plane); } static planeContainsPolyLine(pline, plane) { return Ratchet2d.planeContainsLines(Ratchet2d.polyLineToLines(pline), plane); } static planeContainsLine(line, plane) { return (Ratchet2d.validPlane(plane) && Ratchet2d.validLine(line) && Ratchet2d.planeContainsPoint(line.p1, plane) && Ratchet2d.planeContainsPoint(line.p2, plane)); } static xToY(points) { let rval = null; if (Ratchet2d.validPoints(points)) { rval = points.map((p) => { const next = { x: p.y, y: p.x }; return next; }); } return rval; } static translate(points, xlate) { let rval = null; if (Ratchet2d.validPoints(points) && Ratchet2d.validPoint(xlate)) { rval = Ratchet2d.applyTransform(points, MatrixFactory.translate(xlate.x, xlate.y)); } return rval; } static mirrorPointsOnPlane(points, src, mirrorX, mirrorY) { let rval = null; if (Ratchet2d.validPoints(points) && Ratchet2d.validPlane(src)) { if (mirrorX || mirrorY) { rval = points.map((p) => { const next = { x: mirrorX ? src.width - 1 - p.x : p.x, y: mirrorY ? src.height - 1 - p.y : p.y, }; return next; }); } else { rval = Object.assign([], points); } } return rval; } static matchingVerticalOrientation(pt1, pt2) { return ((pt1 === Plane2dType.BottomRight || pt1 === Plane2dType.BottomLeft) && (pt2 === Plane2dType.BottomRight || pt2 === Plane2dType.BottomLeft)) || ((pt1 === Plane2dType.TopLeft || pt1 === Plane2dType.TopRight) && (pt2 === Plane2dType.TopLeft || pt2 === Plane2dType.TopRight)); } static matchingHorizontalOrientation(pt1, pt2) { return ((pt1 === Plane2dType.BottomRight || pt1 === Plane2dType.TopRight) && (pt2 === Plane2dType.BottomRight || pt2 === Plane2dType.TopRight)) || ((pt1 === Plane2dType.BottomLeft || pt1 === Plane2dType.TopLeft) && (pt2 === Plane2dType.BottomLeft || pt2 === Plane2dType.TopLeft)); } static transformPointsToNewPlane(points, src, dst) { let rval = null; if (Ratchet2d.validPoints(points) && Ratchet2d.validPlane(src) && Ratchet2d.validPlane(dst)) { const scale = Ratchet2d.scaleVector(src, dst); rval = Ratchet2d.applyTransform(points, MatrixFactory.scale(scale.x, scale.y)); rval = Ratchet2d.mirrorPointsOnPlane(rval, dst, !Ratchet2d.matchingHorizontalOrientation(src.type, dst.type), !Ratchet2d.matchingVerticalOrientation(src.type, dst.type)); } return rval; } static transformPointToNewPlane(point, src, dst) { const tmp = Ratchet2d.transformPointsToNewPlane([point], src, dst); return tmp.length === 1 ? tmp[0] : null; } static transformLines(_lines, _src, _dst) { return null; } static fitCurve(curveDef, inputX) { curveDef.sort((a, b) => { return a.x - b.x; }); if (inputX <= curveDef[0].x) { return curveDef[0].y; } else if (inputX >= curveDef[curveDef.length - 1].x) { return curveDef[curveDef.length - 1].y; } else { let idx = 0; while (curveDef[idx + 1].x < inputX) { idx++; } const xSpread = curveDef[idx + 1].x - curveDef[idx].x; const ySpread = curveDef[idx + 1].y - curveDef[idx].y; const pct = (inputX - curveDef[idx].x) / xSpread; const yAdd = pct * ySpread; return curveDef[idx].y + yAdd; } } static midPoint(src, other) { return Ratchet2d.pointAtPercentDistance(src, other, 0.5); } static pointAtPercentDistance(src, other, percent) { if (percent < 0 || percent > 1) { throw ErrorRatchet.fErr('Percent must be between 0 and 1, but was %d', percent); } const deltaX = other.x - src.x; const deltaY = other.y - src.y; return { x: src.x + deltaX * percent, y: src.y + deltaY * percent }; } static validatePoint(pt) { RequireRatchet.notNullOrUndefined(pt, 'pt'); RequireRatchet.notNullOrUndefined(pt.x, 'pt.x'); RequireRatchet.notNullOrUndefined(pt.y, 'pt.y'); } static validateMatrix(tx, includeUV) { RequireRatchet.notNullOrUndefined(tx, 'tx'); RequireRatchet.notNullOrUndefined(tx.a, 'tx.a'); RequireRatchet.notNullOrUndefined(tx.b, 'tx.b'); RequireRatchet.notNullOrUndefined(tx.c, 'tx.c'); RequireRatchet.notNullOrUndefined(tx.d, 'tx.d'); if (includeUV) { RequireRatchet.notNullOrUndefined(tx.u, 'tx.u'); RequireRatchet.notNullOrUndefined(tx.v, 'tx.v'); } } static applyTransform(pt, tx) { const rval = pt?.length ? pt.map(p => Ratchet2d.applyTransformSingle(p, tx)) : pt; return rval; } static applyTransformSingle(pt, tx) { Ratchet2d.validatePoint(pt); Ratchet2d.validateMatrix(tx); const rval = { x: (tx.a * pt.x) + (tx.b * pt.y) + (tx.u | 0), y: (tx.c * pt.x) + (tx.d * pt.y) + (tx.v | 0) }; return rval; } } //# sourceMappingURL=ratchet-2d.js.map