@bitblit/ratchet-common
Version:
Common tools for general use
290 lines • 11.6 kB
JavaScript
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