UNPKG

gramoloss

Version:

Graph theory package for edition and computation

249 lines (248 loc) 8.74 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isPrime = exports.booleanArrayToString = exports.isModSquare = exports.det = exports.isQuadraticBezierCurvesIntersection = exports.isTrianglesIntersection = exports.isPointInTriangle = exports.linesIntersection = exports.segmentsInteriorIntersection = exports.segmentsIntersection = exports.isSegmentsIntersection = exports.solveLinearEquation2D = exports.bezierCurvePoint = exports.binomialCoef = exports.bezierValue = exports.eqSet = void 0; const coord_1 = require("./coord"); // --------------------- // decide if there is equality between two sets xs and ys function eqSet(xs, ys) { return xs.size === ys.size && [...xs].every((x) => ys.has(x)); } exports.eqSet = eqSet; /** * (1-t)^2 p0 + 2t(1-t)p1 + t^2 p2 */ function bezierValue(t, p0, p1, p2) { return (1.0 - t) * (1.0 - t) * p0 + 2.0 * (1.0 - t) * t * p1 + t * t * p2; } exports.bezierValue = bezierValue; /** * Compute the binomial coefficient C(n,k) by recurrence * */ function binomialCoef(n, k) { if (k == 0 || k == n) { return 1; } else if (n < k) { return 0; } else { return binomialCoef(n - 1, k) + binomialCoef(n - 1, k - 1); } } exports.binomialCoef = binomialCoef; /** * Compute B(t) where t is in [0,1] and B is a Bezier curve given by its list of points * @param t in [0,1] * @param points * @returns Sum( points[i]*t^i*Binom(n,i)) */ function bezierCurvePoint(t, points) { const n = points.length - 1; const q = 1 - t; let r = new coord_1.Coord(0, 0); let ti = 1; // ti = t^i let qi = q ** n; // qi = q^(n-i) for (let i = 0; i <= n; i++) { const cni = binomialCoef(n, i); r.x += cni * qi * ti * points[i].x; r.y += cni * qi * ti * points[i].y; ti *= t; qi /= q; } return r; } exports.bezierCurvePoint = bezierCurvePoint; // --------------------- // Solve equation t u + t'v = c where u, v and c are 2d vectors // return false if there is no solution function solveLinearEquation2D(u, v, c) { const det = u.x * v.y - u.y * v.x; if (det == 0) { return false; } const t1 = (c.x * v.y - c.y * v.x) / det; const t2 = (c.y * u.x - c.x * u.y) / det; return [t1, t2]; } exports.solveLinearEquation2D = solveLinearEquation2D; // --------------------- // TODO : check the 0.01 precision /** * Search for an intersection between the segments [a,b] and [c,d]. * Returns an Option with the coord of the intersection if it exists. */ function isSegmentsIntersection(a, b, c, d) { const r = segmentsIntersection(a, b, c, d); return typeof r === "undefined" ? false : true; } exports.isSegmentsIntersection = isSegmentsIntersection; /** * UNTESTED * Search for an intersection between the segments [a,b] and [c,d]. * Returns an Option with the coord of the intersection if it exists. */ function segmentsIntersection(a, b, c, d) { const det = (a.x - b.x) * (d.y - c.y) - (a.y - b.y) * (d.x - c.x); if (det == 0) { return undefined; } const t1 = ((d.x - b.x) * (d.y - c.y) + (d.y - b.y) * (-(d.x - c.x))) / det; const t2 = ((d.x - b.x) * (-(a.y - b.y)) + (d.y - b.y) * (a.x - b.x)) / det; const condition = 0 <= t1 && t1 <= 1 && 0 <= t2 && t2 <= 1; if (condition) { return new coord_1.Coord(b.x + t1 * (a.x - b.x), b.y + t1 * (a.y - b.y)); } else { return undefined; } } exports.segmentsIntersection = segmentsIntersection; /** * UNTESTED * Search for an intersection between the segments ]a,b[ and ]c,d[. * Returns an Option with the coord of the intersection if it exists. */ function segmentsInteriorIntersection(a, b, c, d) { const e = 0.00001; const det = (a.x - b.x) * (d.y - c.y) - (a.y - b.y) * (d.x - c.x); if (det == 0) { return undefined; } const t1 = ((d.x - b.x) * (d.y - c.y) + (d.y - b.y) * (-(d.x - c.x))) / det; const t2 = ((d.x - b.x) * (-(a.y - b.y)) + (d.y - b.y) * (a.x - b.x)) / det; const condition = 0 + e < t1 && t1 < 1 - e && 0 + e < t2 && t2 < 1 - e; if (condition) { return new coord_1.Coord(b.x + t1 * (a.x - b.x), b.y + t1 * (a.y - b.y)); } else { return undefined; } } exports.segmentsInteriorIntersection = segmentsInteriorIntersection; /** * UNTESTED * Search for an intersection between the lines (AB) and (CD). * Returns an Option with the coord of the intersection if it exists. */ function linesIntersection(a, b, c, d) { const det = (a.x - b.x) * (d.y - c.y) - (a.y - b.y) * (d.x - c.x); if (det == 0) { return undefined; } const t1 = ((d.x - b.x) * (d.y - c.y) + (d.y - b.y) * (-(d.x - c.x))) / det; return new coord_1.Coord(b.x + t1 * (a.x - b.x), b.y + t1 * (a.y - b.y)); } exports.linesIntersection = linesIntersection; // --------------------- // Given a point and triangle defined by its three corners q1, q2 and q3 // Returns true iff point is in the triangle function isPointInTriangle(point, q1, q2, q3) { const sol = solveLinearEquation2D(q1.sub(q3), q2.sub(q3), point.sub(q3)); if (typeof sol == "boolean") { return false; } else { const r = sol[0]; const s = sol[1]; return 0 <= r && 0 <= s && 0 <= 1 - r - s; } } exports.isPointInTriangle = isPointInTriangle; // --------------------- // return true iff there is an intersection between two triangles given by their corners // this includes convex hull intersection (if a triangle is included in the other triangle) function isTrianglesIntersection(p1, p2, p3, q1, q2, q3) { if (isPointInTriangle(p1, q1, q2, q3) && isPointInTriangle(p2, q1, q2, q3) && isPointInTriangle(p3, q1, q2, q3)) { return true; } if (isPointInTriangle(q1, p1, p2, p3) && isPointInTriangle(q2, p1, p2, p3) && isPointInTriangle(q3, p1, p2, p3)) { return true; } if (isSegmentsIntersection(p1, p2, q1, q2) || isSegmentsIntersection(p1, p2, q1, q3) || isSegmentsIntersection(p1, p2, q2, q3) || isSegmentsIntersection(p1, p3, q1, q2) || isSegmentsIntersection(p1, p3, q1, q3) || isSegmentsIntersection(p1, p3, q2, q3) || isSegmentsIntersection(p3, p2, q1, q2) || isSegmentsIntersection(p3, p2, q1, q3) || isSegmentsIntersection(p3, p2, q2, q3)) { return true; } return false; } exports.isTrianglesIntersection = isTrianglesIntersection; // -------------------- // Decide if there is an intersection between two quadratic Bezier curves function isQuadraticBezierCurvesIntersection(p1, cp1, p2, q1, cp2, q2) { if (isTrianglesIntersection(p1, cp1, p2, q1, cp2, q2) == false) { return false; } const m = 10; const bezier1 = new Array(p1, cp1, p2); const bezier2 = new Array(q1, cp2, q2); for (let i = 0; i < m; i++) { const a = bezierCurvePoint(i / m, bezier1); const b = bezierCurvePoint((i + 1) / m, bezier1); for (let j = 0; j <= m; j++) { const c = bezierCurvePoint(j / m, bezier2); const d = bezierCurvePoint((j + 1) / m, bezier2); if (isSegmentsIntersection(a, b, c, d)) { return true; } } } return false; } exports.isQuadraticBezierCurvesIntersection = isQuadraticBezierCurvesIntersection; /** * Returns the determinant of the matrix. * Algorithm: Leibniz formula (recursive). * @param matrix is supposed to be square and not of size 0 * No error are triggered it is not the case. */ function det(matrix) { const n = matrix.length; if (n === 1) { return matrix[0][0]; } let d = 0; for (let i = 0; i < n; i++) { const subMatrix = matrix.slice(1).map(row => row.filter((_, j) => j !== i)); const sign = i % 2 === 0 ? 1 : -1; d += sign * matrix[0][i] * det(subMatrix); } return d; } exports.det = det; /** * Return true if n is a square modulo m. * @param n integer * @param m integer * @example * isModSquare(4,5) == true // because 4 = 2^2 mod 5 * isModSquare(3,5) == false // because 0, 1 and 4 are the only squares in Z/5Z. */ function isModSquare(n, m) { for (let i = 0; i < m; i++) { if ((n - i * i) % m == 0) { return true; } } return false; } exports.isModSquare = isModSquare; function booleanArrayToString(array) { return array.map((value) => value ? '1' : '0').join(''); } exports.booleanArrayToString = booleanArrayToString; function isPrime(num) { if (num <= 1) return false; for (let i = 2; i <= Math.sqrt(num); i++) { if (num % i === 0) return false; } return true; } exports.isPrime = isPrime;