@stemy/ngx-utils 19.7.1 → 19.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/stemy-ngx-utils.mjs +444 -262
- package/fesm2022/stemy-ngx-utils.mjs.map +1 -1
- package/ngx-utils/common-types.d.ts +36 -4
- package/ngx-utils/components/interactive-canvas/interactive-canvas.component.d.ts +27 -14
- package/ngx-utils/components/interactive-canvas/interactive-item.component.d.ts +4 -0
- package/ngx-utils/ngx-utils.imports.d.ts +2 -2
- package/ngx-utils/utils/canvas.d.ts +1 -0
- package/ngx-utils/utils/geometry/functions.d.ts +12 -5
- package/ngx-utils/utils/geometry/gjk.d.ts +3 -0
- package/ngx-utils/utils/geometry/index.d.ts +2 -1
- package/ngx-utils/utils/geometry/shapes.d.ts +15 -8
- package/ngx-utils/utils/math.utils.d.ts +34 -0
- package/package.json +1 -1
- package/public_api.d.ts +3 -3
- package/ngx-utils/utils/geometry/distance.d.ts +0 -25
|
@@ -1146,6 +1146,9 @@ function drawOval(ctx, w, h) {
|
|
|
1146
1146
|
ctx.ellipse(0, 0, w / 2, h / 2, 0, 0, Math.PI * 2);
|
|
1147
1147
|
ctx.closePath();
|
|
1148
1148
|
}
|
|
1149
|
+
function drawPoint(ctx) {
|
|
1150
|
+
drawOval(ctx, 4, 4);
|
|
1151
|
+
}
|
|
1149
1152
|
class CanvasUtils {
|
|
1150
1153
|
static manipulatePixels(canvas, ctx, colorTransformer) {
|
|
1151
1154
|
const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
|
@@ -1732,28 +1735,54 @@ class FileSystemEntry {
|
|
|
1732
1735
|
function dotProduct(a, b) {
|
|
1733
1736
|
return a.x * b.x + a.y * b.y;
|
|
1734
1737
|
}
|
|
1738
|
+
function tripleProduct(a, b, c) {
|
|
1739
|
+
const ac = a.x * c.x + a.y * c.y;
|
|
1740
|
+
const bc = b.x * c.x + b.y * c.y;
|
|
1741
|
+
return { x: b.x * ac - a.x * bc, y: b.y * ac - a.y * bc };
|
|
1742
|
+
}
|
|
1735
1743
|
function isPoint(v) {
|
|
1736
|
-
return typeof v === "object" &&
|
|
1744
|
+
return typeof v === "object" && Number.isFinite(v.x) && Number.isFinite(v.y);
|
|
1745
|
+
}
|
|
1746
|
+
function ensurePoint(p, fallback = { x: 0, y: 0 }) {
|
|
1747
|
+
return isPoint(p) ? { x: +p.x, y: +p.y } : fallback;
|
|
1737
1748
|
}
|
|
1738
1749
|
function perpendicular(p) {
|
|
1739
1750
|
return { x: -p.y, y: +p.x };
|
|
1740
1751
|
}
|
|
1741
|
-
function
|
|
1752
|
+
function negatePt(p) {
|
|
1753
|
+
return { x: -p.x, y: -p.y };
|
|
1754
|
+
}
|
|
1755
|
+
function normalizePt(p) {
|
|
1756
|
+
const length = lengthOfPt(p);
|
|
1757
|
+
return dividePts(p, length);
|
|
1758
|
+
}
|
|
1759
|
+
function addPts(a, b) {
|
|
1742
1760
|
return { x: a.x + b.x, y: a.y + b.y };
|
|
1743
1761
|
}
|
|
1744
|
-
function
|
|
1762
|
+
function distanceSq(a, b) {
|
|
1745
1763
|
const x = b.x - a.x;
|
|
1746
1764
|
const y = b.y - a.y;
|
|
1747
|
-
return
|
|
1765
|
+
return x * x + y * y;
|
|
1766
|
+
}
|
|
1767
|
+
function distance(a, b) {
|
|
1768
|
+
return Math.sqrt(distanceSq(a, b));
|
|
1748
1769
|
}
|
|
1749
|
-
function
|
|
1770
|
+
function lerpPts(a, b, t) {
|
|
1771
|
+
const diff = subPts(b, a);
|
|
1772
|
+
return addPts(a, multiplyPts(diff, t));
|
|
1773
|
+
}
|
|
1774
|
+
function lengthOfPt(p) {
|
|
1750
1775
|
return Math.hypot(p.x, p.y);
|
|
1751
1776
|
}
|
|
1752
|
-
function
|
|
1777
|
+
function multiplyPts(a, b) {
|
|
1753
1778
|
const s = isPoint(b) ? b : { x: b, y: b };
|
|
1754
1779
|
return { x: a.x * s.x, y: a.y * s.y };
|
|
1755
1780
|
}
|
|
1756
|
-
function
|
|
1781
|
+
function dividePts(a, b) {
|
|
1782
|
+
const s = isPoint(b) ? b : { x: b, y: b };
|
|
1783
|
+
return { x: a.x / s.x, y: a.y / s.y };
|
|
1784
|
+
}
|
|
1785
|
+
function subPts(a, b) {
|
|
1757
1786
|
return { x: a.x - b.x, y: a.y - b.y };
|
|
1758
1787
|
}
|
|
1759
1788
|
function rotateDeg(p, ang) {
|
|
@@ -1770,125 +1799,261 @@ function toRadians(deg) {
|
|
|
1770
1799
|
return deg * Math.PI / 180;
|
|
1771
1800
|
}
|
|
1772
1801
|
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1802
|
+
const EPSILON = 1e-9;
|
|
1803
|
+
/**
|
|
1804
|
+
* Normalize a range
|
|
1805
|
+
* @param minOrRange
|
|
1806
|
+
* @param max
|
|
1807
|
+
*/
|
|
1808
|
+
function normalizeRange(minOrRange, max) {
|
|
1809
|
+
const min = (Array.isArray(minOrRange) ? minOrRange[0] : minOrRange) ?? 0;
|
|
1810
|
+
max = (Array.isArray(minOrRange) ? minOrRange[1] : max) ?? 1;
|
|
1811
|
+
return (max < min) ? [max, min] : [min, max];
|
|
1778
1812
|
}
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1813
|
+
/**
|
|
1814
|
+
* Clamps a value to a range
|
|
1815
|
+
* @param value
|
|
1816
|
+
* @param min
|
|
1817
|
+
* @param max
|
|
1818
|
+
*/
|
|
1819
|
+
function clamp(value, min, max) {
|
|
1820
|
+
const range = normalizeRange(min, max);
|
|
1821
|
+
return Math.max(Math.min(value, range[1]), range[0]);
|
|
1784
1822
|
}
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
return { simplex: [B], closest: B.p, bary: [1] };
|
|
1796
|
-
}
|
|
1797
|
-
const t = Math.max(0, Math.min(1, -dotProduct(A.p, ab) / ab2));
|
|
1798
|
-
const closest = ptAdd(A.p, ptMultiply(ab, t));
|
|
1799
|
-
if (t <= 1e-9) {
|
|
1800
|
-
return { simplex: [A], closest: A.p, bary: [1] };
|
|
1801
|
-
}
|
|
1802
|
-
if (t >= 1 - 1e-9) {
|
|
1803
|
-
return { simplex: [B], closest: B.p, bary: [1] };
|
|
1804
|
-
}
|
|
1805
|
-
return { simplex: [A, B], closest, bary: [1 - t, t] };
|
|
1806
|
-
}
|
|
1807
|
-
// Triangle case (A,B,C) – use Ericson's closest-point to triangle (p=origin)
|
|
1808
|
-
const A = simplex[0], B = simplex[1], C = simplex[2];
|
|
1809
|
-
const a = A.p, b = B.p, c = C.p;
|
|
1810
|
-
const ab = ptSubtract(b, a), ac = ptSubtract(c, a), ap = { x: -a.x, y: -a.y };
|
|
1811
|
-
const d1 = dotProduct(ab, ap), d2 = dotProduct(ac, ap);
|
|
1812
|
-
if (d1 <= 0 && d2 <= 0)
|
|
1813
|
-
return { simplex: [A], closest: a, bary: [1] };
|
|
1814
|
-
const bp = { x: -b.x, y: -b.y };
|
|
1815
|
-
const d3 = dotProduct(ab, bp), d4 = dotProduct(ac, bp);
|
|
1816
|
-
if (d3 >= 0 && d4 <= d3)
|
|
1817
|
-
return { simplex: [B], closest: b, bary: [1] };
|
|
1818
|
-
const vc = d1 * d4 - d3 * d2;
|
|
1819
|
-
if (vc <= 0 && d1 >= 0 && d3 <= 0) {
|
|
1820
|
-
const v = d1 / (d1 - d3);
|
|
1821
|
-
const closest = ptAdd(a, ptMultiply(ab, v));
|
|
1822
|
-
return { simplex: [A, B], closest, bary: [1 - v, v] };
|
|
1823
|
-
}
|
|
1824
|
-
const cp = { x: -c.x, y: -c.y };
|
|
1825
|
-
const bc = ptSubtract(c, b);
|
|
1826
|
-
const d5 = dotProduct(bc, cp), d6 = dotProduct(ac, cp);
|
|
1827
|
-
if (d6 >= 0 && d5 <= d6)
|
|
1828
|
-
return { simplex: [C], closest: c, bary: [1] };
|
|
1829
|
-
const vb = d5 * d2 - d1 * d6;
|
|
1830
|
-
if (vb <= 0 && d2 >= 0 && d6 <= 0) {
|
|
1831
|
-
const w = d2 / (d2 - d6);
|
|
1832
|
-
const closest = ptAdd(a, ptMultiply(ac, w));
|
|
1833
|
-
return { simplex: [A, C], closest, bary: [1 - w, 0, w] };
|
|
1834
|
-
}
|
|
1835
|
-
const va = d3 * d6 - d5 * d4;
|
|
1836
|
-
if (va <= 0) {
|
|
1837
|
-
const denom = (d4 - d3) + (d5 - d6);
|
|
1838
|
-
const w = denom !== 0 ? (d4 - d3) / denom : 0.5;
|
|
1839
|
-
const closest = ptAdd(b, ptMultiply(bc, w));
|
|
1840
|
-
return { simplex: [B, C], closest, bary: [0, 1 - w, w] };
|
|
1841
|
-
}
|
|
1842
|
-
// Origin inside triangle – distance is zero
|
|
1843
|
-
return { simplex: [A, B, C], closest: { x: 0, y: 0 }, bary: [0, 0, 0] };
|
|
1823
|
+
/**
|
|
1824
|
+
* Clamps a value to a range in a way, that when it is over in one end, then it appears from the other end
|
|
1825
|
+
* @param value
|
|
1826
|
+
* @param min
|
|
1827
|
+
* @param max
|
|
1828
|
+
*/
|
|
1829
|
+
function overflow(value, min, max) {
|
|
1830
|
+
const range = normalizeRange(min, max);
|
|
1831
|
+
const length = range[1] - range[0];
|
|
1832
|
+
return ((((value - range[0]) % length) + length) % length) + range[0];
|
|
1844
1833
|
}
|
|
1845
1834
|
/**
|
|
1846
|
-
*
|
|
1847
|
-
*
|
|
1848
|
-
* @param
|
|
1849
|
-
* @param
|
|
1835
|
+
* Checks if a number is equal to b number with epsilon tolerance
|
|
1836
|
+
* @param a
|
|
1837
|
+
* @param b
|
|
1838
|
+
* @param epsilon
|
|
1850
1839
|
*/
|
|
1840
|
+
function isEqual(a, b, epsilon = null) {
|
|
1841
|
+
epsilon = ObjectUtils.isNumber(epsilon) ? epsilon : EPSILON;
|
|
1842
|
+
return Math.abs(a - b) <= epsilon;
|
|
1843
|
+
}
|
|
1844
|
+
/**
|
|
1845
|
+
* Checks if a number is equal to zero with epsilon tolerance
|
|
1846
|
+
* @param a
|
|
1847
|
+
* @param epsilon
|
|
1848
|
+
*/
|
|
1849
|
+
function isZero(a, epsilon = null) {
|
|
1850
|
+
return isEqual(a, 0, epsilon);
|
|
1851
|
+
}
|
|
1852
|
+
class MathUtils {
|
|
1853
|
+
static equal(a, b, epsilon = null) {
|
|
1854
|
+
return isEqual(a, b, epsilon);
|
|
1855
|
+
}
|
|
1856
|
+
static clamp(value, min, max) {
|
|
1857
|
+
return clamp(value, min, max);
|
|
1858
|
+
}
|
|
1859
|
+
static round(value, precision = 2, divider = 1) {
|
|
1860
|
+
precision = Math.pow(10, precision);
|
|
1861
|
+
return Math.round(value * precision / divider) / precision;
|
|
1862
|
+
}
|
|
1863
|
+
static approxIndex(x, values, epsilon = null) {
|
|
1864
|
+
if (!Array.isArray(values) || values.length == 0) {
|
|
1865
|
+
return -1;
|
|
1866
|
+
}
|
|
1867
|
+
let s = 0;
|
|
1868
|
+
let e = values.length - 1;
|
|
1869
|
+
while (s <= e) {
|
|
1870
|
+
const i = Math.floor((s + e) / 2);
|
|
1871
|
+
const v = values[i];
|
|
1872
|
+
if (MathUtils.equal(v, x, epsilon)) {
|
|
1873
|
+
return i;
|
|
1874
|
+
}
|
|
1875
|
+
if (v < x) {
|
|
1876
|
+
s = i + 1;
|
|
1877
|
+
}
|
|
1878
|
+
else {
|
|
1879
|
+
e = i - 1;
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
const m = Math.max(e, 0);
|
|
1883
|
+
const a = values[s];
|
|
1884
|
+
const b = values[m];
|
|
1885
|
+
return Math.abs(a - x) < Math.abs(b - x) ? s : m;
|
|
1886
|
+
}
|
|
1887
|
+
static approximate(x, values, epsilon = null) {
|
|
1888
|
+
const index = MathUtils.approxIndex(x, values, epsilon);
|
|
1889
|
+
return values[index] ?? null;
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
const MAX_ITERS = 40;
|
|
1894
|
+
// =========================
|
|
1895
|
+
// GJK distance (robust)
|
|
1896
|
+
// =========================
|
|
1851
1897
|
function gjkDistance(A, B) {
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1898
|
+
let intersection = gjkIntersection(A, B);
|
|
1899
|
+
if (intersection.hit)
|
|
1900
|
+
return { distance: 0 };
|
|
1901
|
+
const ca = A.center;
|
|
1902
|
+
const cb = B.center;
|
|
1903
|
+
let s = 0;
|
|
1904
|
+
let e = 1;
|
|
1905
|
+
let center = ca;
|
|
1906
|
+
let iters = 0;
|
|
1907
|
+
while (e - s > EPSILON) {
|
|
1908
|
+
iters++;
|
|
1909
|
+
const t = (e + s) / 2;
|
|
1910
|
+
const a = A.move(lerpPts(ca, cb, t));
|
|
1911
|
+
const test = gjkIntersection(a, B);
|
|
1912
|
+
center = a.center;
|
|
1913
|
+
if (test.hit) {
|
|
1914
|
+
intersection = test;
|
|
1915
|
+
e = t;
|
|
1916
|
+
if (iters >= MAX_ITERS)
|
|
1917
|
+
break;
|
|
1918
|
+
}
|
|
1919
|
+
else {
|
|
1920
|
+
s = t;
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
const result = distance(ca, center);
|
|
1924
|
+
return {
|
|
1925
|
+
distance: result,
|
|
1926
|
+
pa: result > 0 ? addPts(intersection.pa, subPts(ca, center)) : null,
|
|
1927
|
+
pb: result > 0 ? intersection.pb : null
|
|
1928
|
+
};
|
|
1929
|
+
}
|
|
1930
|
+
// =========================
|
|
1931
|
+
// Boolean GJK (robust)
|
|
1932
|
+
// =========================
|
|
1933
|
+
function gjkIntersection(A, B) {
|
|
1934
|
+
const MAX = 64, EPS = 1e-12;
|
|
1935
|
+
const sup = (dir) => {
|
|
1936
|
+
const a = ensurePoint(A.support(dir), A.center);
|
|
1937
|
+
const b = ensurePoint(B.support(negatePt(dir)), B.center);
|
|
1938
|
+
return { p: subPts(a, b), a, b };
|
|
1939
|
+
};
|
|
1940
|
+
// initial direction: center-to-center; fall back to x-axis
|
|
1941
|
+
let d = subPts(B.center, A.center);
|
|
1855
1942
|
if (Math.abs(d.x) < EPS && Math.abs(d.y) < EPS)
|
|
1856
1943
|
d = { x: 1, y: 0 };
|
|
1857
|
-
const
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1944
|
+
const simplex = [sup(d)];
|
|
1945
|
+
d = { x: -simplex[0].p.x, y: -simplex[0].p.y };
|
|
1946
|
+
for (let i = 0; i < MAX; i++) {
|
|
1947
|
+
// If direction collapses, steer toward origin from last point
|
|
1948
|
+
const dLen = Math.hypot(d.x, d.y);
|
|
1949
|
+
if (dLen <= EPS) {
|
|
1950
|
+
const last = simplex[simplex.length - 1];
|
|
1951
|
+
const AO = { x: -last.p.x, y: -last.p.y };
|
|
1952
|
+
const aoLen = Math.hypot(AO.x, AO.y);
|
|
1953
|
+
d = (aoLen > EPS) ? AO : { x: 1, y: 0 };
|
|
1954
|
+
}
|
|
1955
|
+
const a = sup(d);
|
|
1956
|
+
const s = a.p.x * d.x + a.p.y * d.y;
|
|
1957
|
+
if (s < -1e-12)
|
|
1958
|
+
return { hit: false }; // definite separation
|
|
1959
|
+
if (Math.abs(s) <= 1e-12) { // tangential contact: use normal ±d
|
|
1960
|
+
const L = Math.hypot(d.x, d.y) || 1;
|
|
1961
|
+
const n = { x: d.x / L, y: d.y / L };
|
|
1962
|
+
const pa = ensurePoint(A.support(n), A.center);
|
|
1963
|
+
const pb = ensurePoint(B.support(negatePt(n)), B.center);
|
|
1964
|
+
const point = { x: (pa.x + pb.x) / 2, y: (pa.y + pb.y) / 2 };
|
|
1965
|
+
return { hit: true, pa, pb, point };
|
|
1966
|
+
}
|
|
1967
|
+
simplex.push(a);
|
|
1968
|
+
{
|
|
1969
|
+
const info = doSimplexBoolean(simplex, d);
|
|
1970
|
+
if (info.hit)
|
|
1971
|
+
return info;
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
// Max iterations without resolution → disjoint
|
|
1975
|
+
return { hit: false };
|
|
1976
|
+
}
|
|
1977
|
+
function doSimplexBoolean(simplex, d) {
|
|
1978
|
+
const last = simplex[simplex.length - 1];
|
|
1979
|
+
const AO = { x: -last.p.x, y: -last.p.y };
|
|
1980
|
+
if (simplex.length === 2) {
|
|
1981
|
+
const B = simplex[0];
|
|
1982
|
+
const AB = { x: B.p.x - last.p.x, y: B.p.y - last.p.y };
|
|
1983
|
+
// Perpendicular to AB toward origin
|
|
1984
|
+
const abPerp = tripleProduct(AB, AO, AB);
|
|
1985
|
+
const perpLen2 = abPerp.x * abPerp.x + abPerp.y * abPerp.y;
|
|
1986
|
+
if (perpLen2 < 1e-24) {
|
|
1987
|
+
// Colinear: project origin onto segment AB
|
|
1988
|
+
const ab2 = AB.x * AB.x + AB.y * AB.y;
|
|
1989
|
+
if (ab2 > 0) {
|
|
1990
|
+
const t = ((AO.x * AB.x + AO.y * AB.y) / ab2);
|
|
1991
|
+
if (t >= 0 && t <= 1) {
|
|
1992
|
+
// Contact at segment
|
|
1993
|
+
const pa = { x: last.a.x + t * (B.a.x - last.a.x), y: last.a.y + t * (B.a.y - last.a.y) };
|
|
1994
|
+
const pb = { x: last.b.x + t * (B.b.x - last.b.x), y: last.b.y + t * (B.b.y - last.b.y) };
|
|
1995
|
+
const point = { x: (pa.x + pb.x) / 2, y: (pa.y + pb.y) / 2 };
|
|
1996
|
+
return { hit: true, pa, pb, point };
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
// Otherwise, move toward origin from A, keep only last point to progress
|
|
2000
|
+
d.x = -AO.x;
|
|
2001
|
+
d.y = -AO.y;
|
|
2002
|
+
simplex.splice(0, simplex.length - 1);
|
|
2003
|
+
return { hit: false };
|
|
2004
|
+
}
|
|
2005
|
+
d.x = abPerp.x;
|
|
2006
|
+
d.y = abPerp.y;
|
|
2007
|
+
return { hit: false };
|
|
2008
|
+
}
|
|
2009
|
+
// Triangle case: [C, B, A] with A = last
|
|
2010
|
+
const A = last, B = simplex[simplex.length - 2], C = simplex[simplex.length - 3];
|
|
2011
|
+
const AB = { x: B.p.x - A.p.x, y: B.p.y - A.p.y };
|
|
2012
|
+
const AC = { x: C.p.x - A.p.x, y: C.p.y - A.p.y };
|
|
2013
|
+
const ABperp = tripleProduct(AC, AB, AB);
|
|
2014
|
+
const ACperp = tripleProduct(AB, AC, AC);
|
|
2015
|
+
// If origin is outside AB region
|
|
2016
|
+
if (ABperp.x * AO.x + ABperp.y * AO.y > 0) {
|
|
2017
|
+
simplex.splice(simplex.length - 3, 1);
|
|
2018
|
+
d.x = ABperp.x;
|
|
2019
|
+
d.y = ABperp.y;
|
|
2020
|
+
return { hit: false };
|
|
2021
|
+
}
|
|
2022
|
+
// If origin is outside AC region
|
|
2023
|
+
if (ACperp.x * AO.x + ACperp.y * AO.y > 0) {
|
|
2024
|
+
simplex.splice(simplex.length - 2, 1);
|
|
2025
|
+
d.x = ACperp.x;
|
|
2026
|
+
d.y = ACperp.y;
|
|
2027
|
+
return { hit: false };
|
|
2028
|
+
}
|
|
2029
|
+
// Otherwise the origin is inside the triangle → overlap
|
|
2030
|
+
// Compute barycentric weights of origin w.r.t triangle (A,B,C)
|
|
2031
|
+
const v0 = AB; // B-A
|
|
2032
|
+
const v1 = AC; // C-A
|
|
2033
|
+
const b = { x: -A.p.x, y: -A.p.y };
|
|
2034
|
+
const det = v0.x * v1.y - v0.y * v1.x;
|
|
2035
|
+
let uA, uB, uC;
|
|
2036
|
+
if (Math.abs(det) > 1e-24) {
|
|
2037
|
+
const alpha = (b.x * v1.y - v1.x * b.y) / det; // weight for v0 (B)
|
|
2038
|
+
const beta = (v0.x * b.y - b.x * v0.y) / det; // weight for v1 (C)
|
|
2039
|
+
uB = alpha;
|
|
2040
|
+
uC = beta;
|
|
2041
|
+
uA = 1 - alpha - beta;
|
|
2042
|
+
}
|
|
2043
|
+
else {
|
|
2044
|
+
// Fallback: equal weights
|
|
2045
|
+
uA = uB = uC = 1 / 3;
|
|
2046
|
+
}
|
|
2047
|
+
const pa = {
|
|
2048
|
+
x: uA * A.a.x + uB * B.a.x + uC * C.a.x,
|
|
2049
|
+
y: uA * A.a.y + uB * B.a.y + uC * C.a.y
|
|
2050
|
+
};
|
|
2051
|
+
const pb = {
|
|
2052
|
+
x: uA * A.b.x + uB * B.b.x + uC * C.b.x,
|
|
2053
|
+
y: uA * A.b.y + uB * B.b.y + uC * C.b.y
|
|
1861
2054
|
};
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
let dir = { x: -closest.x, y: -closest.y };
|
|
1865
|
-
let best2 = dotProduct(closest, closest);
|
|
1866
|
-
for (let iter = 0; iter < MAX; iter++) {
|
|
1867
|
-
if (ptLength(dir) <= EPS) { // origin reached
|
|
1868
|
-
const a = simplex[simplex.length - 1].a, b = simplex[simplex.length - 1].b;
|
|
1869
|
-
return { distance: 0, pa: a, pb: b };
|
|
1870
|
-
}
|
|
1871
|
-
const vtx = sup(dir);
|
|
1872
|
-
// termination: support didn't pass beyond previous closest along dir
|
|
1873
|
-
if (dotProduct(vtx.p, dir) - Math.sqrt(best2) <= EPS) {
|
|
1874
|
-
break;
|
|
1875
|
-
}
|
|
1876
|
-
simplex.push(vtx);
|
|
1877
|
-
const reduced = closestPointToOrigin(simplex);
|
|
1878
|
-
simplex = reduced.simplex;
|
|
1879
|
-
closest = reduced.closest;
|
|
1880
|
-
best2 = dotProduct(closest, closest);
|
|
1881
|
-
dir = { x: -closest.x, y: -closest.y };
|
|
1882
|
-
if (best2 <= EPS * EPS) {
|
|
1883
|
-
const pa = combineA(simplex, reduced.bary);
|
|
1884
|
-
const pb = combineB(simplex, reduced.bary);
|
|
1885
|
-
return { distance: 0, pa, pb };
|
|
1886
|
-
}
|
|
1887
|
-
}
|
|
1888
|
-
const res = closestPointToOrigin(simplex);
|
|
1889
|
-
const pa = combineA(simplex, res.bary);
|
|
1890
|
-
const pb = combineB(simplex, res.bary);
|
|
1891
|
-
return { distance: Math.sqrt(dotProduct(res.closest, res.closest)), pa, pb };
|
|
2055
|
+
const point = { x: (pa.x + pb.x) / 2, y: (pa.y + pb.y) / 2 };
|
|
2056
|
+
return { hit: true, pa, pb, point };
|
|
1892
2057
|
}
|
|
1893
2058
|
|
|
1894
2059
|
class Shape {
|
|
@@ -1904,17 +2069,23 @@ class Shape {
|
|
|
1904
2069
|
constructor(x, y) {
|
|
1905
2070
|
this.pt = { x, y };
|
|
1906
2071
|
}
|
|
1907
|
-
|
|
1908
|
-
return
|
|
2072
|
+
intersection(shape) {
|
|
2073
|
+
return gjkIntersection(this, shape);
|
|
2074
|
+
}
|
|
2075
|
+
intersects(shape) {
|
|
2076
|
+
return this.intersection(shape).hit;
|
|
1909
2077
|
}
|
|
1910
2078
|
minDistance(shape) {
|
|
1911
|
-
return gjkDistance(this, shape)
|
|
2079
|
+
return gjkDistance(this, shape);
|
|
2080
|
+
}
|
|
2081
|
+
distance(shape) {
|
|
2082
|
+
return this.minDistance(shape).distance;
|
|
1912
2083
|
}
|
|
1913
2084
|
}
|
|
1914
2085
|
class Point extends Shape {
|
|
1915
2086
|
static { this.Zero = new Point(0, 0); }
|
|
1916
2087
|
get length() {
|
|
1917
|
-
return
|
|
2088
|
+
return lengthOfPt(this);
|
|
1918
2089
|
}
|
|
1919
2090
|
get perpendicular() {
|
|
1920
2091
|
return new Point(perpendicular(this));
|
|
@@ -1925,32 +2096,35 @@ class Point extends Shape {
|
|
|
1925
2096
|
this.pt = isPoint(xOrP) ? xOrP : { x: isNaN(x) ? 0 : xOrP, y };
|
|
1926
2097
|
}
|
|
1927
2098
|
support() {
|
|
1928
|
-
return this.
|
|
2099
|
+
return this.center;
|
|
2100
|
+
}
|
|
2101
|
+
move(pos) {
|
|
2102
|
+
return new Point(pos);
|
|
1929
2103
|
}
|
|
1930
2104
|
add(p) {
|
|
1931
|
-
return new Point(
|
|
2105
|
+
return new Point(addPts(this, p));
|
|
2106
|
+
}
|
|
2107
|
+
subtract(p) {
|
|
2108
|
+
return new Point(subPts(this, p));
|
|
1932
2109
|
}
|
|
1933
|
-
|
|
1934
|
-
return new Point(
|
|
2110
|
+
multiply(p) {
|
|
2111
|
+
return new Point(multiplyPts(this, p));
|
|
1935
2112
|
}
|
|
1936
|
-
|
|
1937
|
-
return new Point(
|
|
2113
|
+
divide(p) {
|
|
2114
|
+
return new Point(multiplyPts(this, p));
|
|
1938
2115
|
}
|
|
1939
2116
|
dot(p) {
|
|
1940
2117
|
return new Point(dotProduct(this, p));
|
|
1941
2118
|
}
|
|
1942
|
-
distance(p) {
|
|
1943
|
-
return ptDistance(this, p);
|
|
1944
|
-
}
|
|
1945
2119
|
lerp(p, ratio) {
|
|
1946
|
-
const diff = p.
|
|
1947
|
-
return this.add(diff.
|
|
2120
|
+
const diff = p.subtract(this);
|
|
2121
|
+
return this.add(diff.multiply(ratio));
|
|
1948
2122
|
}
|
|
1949
2123
|
perpendicularTo(p, length) {
|
|
1950
|
-
const diff = p.perpendicular.
|
|
2124
|
+
const diff = p.perpendicular.subtract(this.perpendicular);
|
|
1951
2125
|
const ratio = length / diff.length;
|
|
1952
2126
|
const center = this.lerp(p, .5);
|
|
1953
|
-
return center.add(diff.
|
|
2127
|
+
return center.add(diff.multiply(ratio));
|
|
1954
2128
|
}
|
|
1955
2129
|
circleWith(a, b) {
|
|
1956
2130
|
const yDelta_a = b.y - a.y;
|
|
@@ -1965,8 +2139,8 @@ class Point extends Shape {
|
|
|
1965
2139
|
return new Circle(center.x, center.y, center.distance(this));
|
|
1966
2140
|
}
|
|
1967
2141
|
tangents(c) {
|
|
1968
|
-
const pd =
|
|
1969
|
-
const a = Math.asin(c.radius /
|
|
2142
|
+
const pd = subPts(c.center, this);
|
|
2143
|
+
const a = Math.asin(c.radius / lengthOfPt(pd));
|
|
1970
2144
|
const b = Math.atan2(pd.y, pd.x);
|
|
1971
2145
|
// Tangent points
|
|
1972
2146
|
let t = b - a;
|
|
@@ -1976,7 +2150,7 @@ class Point extends Shape {
|
|
|
1976
2150
|
return [t1, t2];
|
|
1977
2151
|
}
|
|
1978
2152
|
angle(p) {
|
|
1979
|
-
const diff = p.
|
|
2153
|
+
const diff = p.subtract(this);
|
|
1980
2154
|
return Math.atan2(diff.y, diff.x) * 180 / Math.PI;
|
|
1981
2155
|
}
|
|
1982
2156
|
rotateAround(p, angle) {
|
|
@@ -1995,11 +2169,16 @@ class Rect extends Shape {
|
|
|
1995
2169
|
}
|
|
1996
2170
|
support(dir) {
|
|
1997
2171
|
const ang = this.rotation ?? 0;
|
|
1998
|
-
const dLocal = rotateDeg(dir, -ang);
|
|
2172
|
+
const dLocal = rotateDeg(ensurePoint(dir, { x: 1, y: 0 }), -ang);
|
|
1999
2173
|
const hw = Math.max(0, this.width / 2), hh = Math.max(0, this.height / 2);
|
|
2174
|
+
if (hw === 0 && hh === 0)
|
|
2175
|
+
return ensurePoint(this.center);
|
|
2000
2176
|
const lx = dLocal.x >= 0 ? hw : -hw;
|
|
2001
2177
|
const ly = dLocal.y >= 0 ? hh : -hh;
|
|
2002
|
-
return
|
|
2178
|
+
return addPts(rotateDeg({ x: lx, y: ly }, ang), this.center);
|
|
2179
|
+
}
|
|
2180
|
+
move(pos) {
|
|
2181
|
+
return new Rect(pos.x, pos.y, this.width, this.height, this.rotation);
|
|
2003
2182
|
}
|
|
2004
2183
|
}
|
|
2005
2184
|
class Oval extends Shape {
|
|
@@ -2011,15 +2190,16 @@ class Oval extends Shape {
|
|
|
2011
2190
|
}
|
|
2012
2191
|
support(dir) {
|
|
2013
2192
|
const ang = this.rotation ?? 0;
|
|
2014
|
-
const d = rotateDeg(dir, -ang);
|
|
2015
|
-
const a = Math.max(0, this.width / 2);
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
const
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2193
|
+
const d = rotateDeg(ensurePoint(dir, { x: 1, y: 0 }), -ang);
|
|
2194
|
+
const a = Math.max(0, this.width / 2), b = Math.max(0, this.height / 2);
|
|
2195
|
+
if (a === 0 && b === 0)
|
|
2196
|
+
return ensurePoint(this.center);
|
|
2197
|
+
const q = Math.hypot(a * d.x, b * d.y) || 1;
|
|
2198
|
+
const lx = (a * a * d.x) / q, ly = (b * b * d.y) / q;
|
|
2199
|
+
return addPts(rotateDeg({ x: lx, y: ly }, ang), this.center);
|
|
2200
|
+
}
|
|
2201
|
+
move(pos) {
|
|
2202
|
+
return new Oval(pos.x, pos.y, this.width, this.height, this.rotation);
|
|
2023
2203
|
}
|
|
2024
2204
|
}
|
|
2025
2205
|
class Circle extends Oval {
|
|
@@ -2027,6 +2207,9 @@ class Circle extends Oval {
|
|
|
2027
2207
|
super(x, y, radius * 2, radius * 2, rotation);
|
|
2028
2208
|
this.radius = radius;
|
|
2029
2209
|
}
|
|
2210
|
+
move(pos) {
|
|
2211
|
+
return new Circle(pos.x, pos.y, this.radius, this.rotation);
|
|
2212
|
+
}
|
|
2030
2213
|
}
|
|
2031
2214
|
|
|
2032
2215
|
class Initializer {
|
|
@@ -2158,49 +2341,6 @@ class LoaderUtils {
|
|
|
2158
2341
|
}
|
|
2159
2342
|
}
|
|
2160
2343
|
|
|
2161
|
-
const EPSILON = 1e-9;
|
|
2162
|
-
class MathUtils {
|
|
2163
|
-
static equal(a, b, epsilon = null) {
|
|
2164
|
-
epsilon = ObjectUtils.isNumber(epsilon) ? epsilon : EPSILON;
|
|
2165
|
-
return Math.abs(a - b) < epsilon;
|
|
2166
|
-
}
|
|
2167
|
-
static clamp(value, min, max) {
|
|
2168
|
-
return Math.max(Math.min(value, max), min);
|
|
2169
|
-
}
|
|
2170
|
-
static round(value, precision = 2, divider = 1) {
|
|
2171
|
-
precision = Math.pow(10, precision);
|
|
2172
|
-
return Math.round(value * precision / divider) / precision;
|
|
2173
|
-
}
|
|
2174
|
-
static approxIndex(x, values, epsilon = null) {
|
|
2175
|
-
if (!Array.isArray(values) || values.length == 0) {
|
|
2176
|
-
return -1;
|
|
2177
|
-
}
|
|
2178
|
-
let s = 0;
|
|
2179
|
-
let e = values.length - 1;
|
|
2180
|
-
while (s <= e) {
|
|
2181
|
-
const i = Math.floor((s + e) / 2);
|
|
2182
|
-
const v = values[i];
|
|
2183
|
-
if (MathUtils.equal(v, x, epsilon)) {
|
|
2184
|
-
return i;
|
|
2185
|
-
}
|
|
2186
|
-
if (v < x) {
|
|
2187
|
-
s = i + 1;
|
|
2188
|
-
}
|
|
2189
|
-
else {
|
|
2190
|
-
e = i - 1;
|
|
2191
|
-
}
|
|
2192
|
-
}
|
|
2193
|
-
const m = Math.max(e, 0);
|
|
2194
|
-
const a = values[s];
|
|
2195
|
-
const b = values[m];
|
|
2196
|
-
return Math.abs(a - x) < Math.abs(b - x) ? s : m;
|
|
2197
|
-
}
|
|
2198
|
-
static approximate(x, values, epsilon = null) {
|
|
2199
|
-
const index = MathUtils.approxIndex(x, values, epsilon);
|
|
2200
|
-
return values[index] ?? null;
|
|
2201
|
-
}
|
|
2202
|
-
}
|
|
2203
|
-
|
|
2204
2344
|
function isBrowser() {
|
|
2205
2345
|
return typeof window !== "undefined";
|
|
2206
2346
|
}
|
|
@@ -7669,29 +7809,31 @@ class InteractiveItemComponent {
|
|
|
7669
7809
|
}
|
|
7670
7810
|
hit(point) {
|
|
7671
7811
|
for (const shape of this.shapes) {
|
|
7672
|
-
if (shape.
|
|
7812
|
+
if (shape.intersects(point))
|
|
7673
7813
|
return true;
|
|
7674
7814
|
}
|
|
7675
7815
|
return false;
|
|
7676
7816
|
}
|
|
7677
|
-
|
|
7678
|
-
if (this.direction === "none")
|
|
7817
|
+
moveTo(x, y) {
|
|
7818
|
+
if (!this.canvas || this.direction === "none")
|
|
7679
7819
|
return;
|
|
7680
|
-
|
|
7681
|
-
|
|
7682
|
-
this.pos = new Point(this.pos.x + dx, this.pos.y);
|
|
7683
|
-
break;
|
|
7684
|
-
case "vertical":
|
|
7685
|
-
this.pos = new Point(this.pos.x, this.pos.y + dy);
|
|
7686
|
-
break;
|
|
7687
|
-
default:
|
|
7688
|
-
this.pos = new Point(this.pos.x + dx, this.pos.y + dy);
|
|
7689
|
-
break;
|
|
7690
|
-
}
|
|
7820
|
+
const target = this.restrictPosition(this.direction === "vertical" ? this.pos.x : x, this.direction === "horizontal" ? this.pos.y : y);
|
|
7821
|
+
this.pos = new Point(target);
|
|
7691
7822
|
this.calcShapes();
|
|
7692
|
-
this.valid = this.isValidByParams() &&
|
|
7823
|
+
this.valid = this.isValidByParams() &&
|
|
7824
|
+
this.canvas.items.every(other => this === other || this.isValidByDistance(other));
|
|
7693
7825
|
this.validPos = this.valid ? this.pos : this.validPos;
|
|
7694
7826
|
}
|
|
7827
|
+
moveBy(dx, dy) {
|
|
7828
|
+
const { x, y } = this.pos;
|
|
7829
|
+
this.moveTo(x + dx, y + dy);
|
|
7830
|
+
}
|
|
7831
|
+
moveX(x) {
|
|
7832
|
+
this.moveTo(x, this.pos.y);
|
|
7833
|
+
}
|
|
7834
|
+
moveY(y) {
|
|
7835
|
+
this.moveTo(this.pos.x, y);
|
|
7836
|
+
}
|
|
7695
7837
|
moveEnd() {
|
|
7696
7838
|
if (this.valid)
|
|
7697
7839
|
return;
|
|
@@ -7699,14 +7841,26 @@ class InteractiveItemComponent {
|
|
|
7699
7841
|
this.valid = true;
|
|
7700
7842
|
this.calcShapes();
|
|
7701
7843
|
}
|
|
7844
|
+
restrictPosition(x, y) {
|
|
7845
|
+
return {
|
|
7846
|
+
x: clamp(x, this.canvas.xRange),
|
|
7847
|
+
y: this.canvas.infinite
|
|
7848
|
+
? overflow(y, this.canvas.yRange)
|
|
7849
|
+
: clamp(y, this.canvas.yRange)
|
|
7850
|
+
};
|
|
7851
|
+
}
|
|
7702
7852
|
isValidByParams() {
|
|
7703
|
-
return
|
|
7853
|
+
return !this.shapes.some(shape => {
|
|
7854
|
+
return this.canvas.exclusions.some(ex => {
|
|
7855
|
+
return shape.distance(ex) < 1;
|
|
7856
|
+
});
|
|
7857
|
+
});
|
|
7704
7858
|
}
|
|
7705
7859
|
isValidByDistance(other) {
|
|
7706
7860
|
const minPixels = this.distToPixels(this.getMinDistance(other));
|
|
7707
7861
|
return !this.shapes.some(shape => {
|
|
7708
7862
|
return other.shapes.some(os => {
|
|
7709
|
-
return
|
|
7863
|
+
return shape.distance(os) <= minPixels;
|
|
7710
7864
|
});
|
|
7711
7865
|
});
|
|
7712
7866
|
}
|
|
@@ -7760,19 +7914,19 @@ class InteractiveCanvasComponent {
|
|
|
7760
7914
|
set hoveredItem(item) {
|
|
7761
7915
|
this.hoveredIndex = !item ? -1 : this.items.indexOf(item);
|
|
7762
7916
|
}
|
|
7763
|
-
constructor(renderer, universal
|
|
7917
|
+
constructor(renderer, universal) {
|
|
7764
7918
|
this.renderer = renderer;
|
|
7765
7919
|
this.universal = universal;
|
|
7766
|
-
this.
|
|
7767
|
-
this.
|
|
7920
|
+
this.infinite = false;
|
|
7921
|
+
this.resizeMode = "fit";
|
|
7922
|
+
this.params = {};
|
|
7768
7923
|
this.debug = false;
|
|
7769
7924
|
this.horizontal = false;
|
|
7770
7925
|
this.selectedIndex = 0;
|
|
7771
|
-
this.resizeMode = "fit";
|
|
7772
7926
|
this.realWidth = 100;
|
|
7773
7927
|
this.realHeight = 100;
|
|
7774
7928
|
this.panOffset = 0;
|
|
7775
|
-
this.
|
|
7929
|
+
this.renderCtx = {};
|
|
7776
7930
|
this.beforeItems = [];
|
|
7777
7931
|
this.afterItems = [];
|
|
7778
7932
|
this.selectedIndexChange = new EventEmitter();
|
|
@@ -7784,16 +7938,21 @@ class InteractiveCanvasComponent {
|
|
|
7784
7938
|
this.$items = new BehaviorSubject([]);
|
|
7785
7939
|
this.tempCanvas = this.universal.isServer ? null : document.createElement("canvas");
|
|
7786
7940
|
this.shouldDraw = !this.universal.isServer;
|
|
7787
|
-
this.
|
|
7941
|
+
this.hoveredIndex = null;
|
|
7942
|
+
this.xRange = [0, 1];
|
|
7943
|
+
this.yRange = [0, 1];
|
|
7944
|
+
this.ratio = 1;
|
|
7945
|
+
this.styles = null;
|
|
7946
|
+
this.ctx = null;
|
|
7788
7947
|
this.canvasWidth = 0;
|
|
7789
7948
|
this.canvasHeight = 0;
|
|
7790
|
-
this.
|
|
7949
|
+
this.rotation = 0;
|
|
7950
|
+
this.basePan = 0;
|
|
7951
|
+
this.cycles = [0];
|
|
7952
|
+
this.exclusions = [];
|
|
7791
7953
|
this.touched = false;
|
|
7792
|
-
this.
|
|
7793
|
-
this.
|
|
7794
|
-
this.ctrInit();
|
|
7795
|
-
}
|
|
7796
|
-
ctrInit() {
|
|
7954
|
+
this.panStartRotation = 0;
|
|
7955
|
+
this.panStartPos = Point.Zero;
|
|
7797
7956
|
}
|
|
7798
7957
|
ngOnInit() {
|
|
7799
7958
|
this.redraw();
|
|
@@ -7807,6 +7966,8 @@ class InteractiveCanvasComponent {
|
|
|
7807
7966
|
this.renderCtx = this.renderCtx || {};
|
|
7808
7967
|
this.beforeItems = this.beforeItems || [];
|
|
7809
7968
|
this.afterItems = this.afterItems || [];
|
|
7969
|
+
this.xRange = normalizeRange(this.params.xRange || [0, this.realWidth]);
|
|
7970
|
+
this.yRange = normalizeRange(this.params.yRange || [0, this.realHeight]);
|
|
7810
7971
|
this.resize();
|
|
7811
7972
|
}
|
|
7812
7973
|
ngAfterViewInit() {
|
|
@@ -7872,20 +8033,21 @@ class InteractiveCanvasComponent {
|
|
|
7872
8033
|
this.updateCursor();
|
|
7873
8034
|
}
|
|
7874
8035
|
onMouseLeave() {
|
|
8036
|
+
if (this.touched)
|
|
8037
|
+
return;
|
|
7875
8038
|
this.hoveredIndex = null;
|
|
7876
8039
|
this.updateCursor();
|
|
7877
8040
|
}
|
|
7878
8041
|
onPanStart() {
|
|
7879
|
-
this.
|
|
7880
|
-
this.
|
|
8042
|
+
this.panStartRotation = this.rotation;
|
|
8043
|
+
this.panStartPos = this.lockedItem?.position || Point.Zero;
|
|
7881
8044
|
}
|
|
7882
8045
|
onPanMove($event) {
|
|
7883
8046
|
const item = this.lockedItem;
|
|
7884
|
-
const deltaX =
|
|
7885
|
-
const deltaY =
|
|
8047
|
+
const deltaX = $event.deltaX / this.ratio;
|
|
8048
|
+
const deltaY = $event.deltaY / this.ratio;
|
|
7886
8049
|
const data = {
|
|
7887
8050
|
canvas: this,
|
|
7888
|
-
pointers: $event.pointers,
|
|
7889
8051
|
item,
|
|
7890
8052
|
deltaX,
|
|
7891
8053
|
deltaY
|
|
@@ -7895,22 +8057,19 @@ class InteractiveCanvasComponent {
|
|
|
7895
8057
|
data.deltaY = +deltaX;
|
|
7896
8058
|
}
|
|
7897
8059
|
if (item) {
|
|
7898
|
-
item.
|
|
8060
|
+
item.moveTo(this.panStartPos.x + data.deltaX, this.panStartPos.y + data.deltaY);
|
|
7899
8061
|
this.onItemPan.emit(data);
|
|
7900
8062
|
}
|
|
7901
|
-
else if (this.
|
|
7902
|
-
this.rotation
|
|
8063
|
+
else if (this.infinite) {
|
|
8064
|
+
this.rotation = this.panStartRotation + (this.horizontal ? deltaX : deltaY) / this.realHeight * 360;
|
|
7903
8065
|
this.fixRotation();
|
|
7904
8066
|
this.onPan.emit(data);
|
|
7905
8067
|
}
|
|
7906
|
-
this.deltaX = $event.deltaX;
|
|
7907
|
-
this.deltaY = $event.deltaY;
|
|
7908
8068
|
}
|
|
7909
8069
|
onPanEnd() {
|
|
7910
8070
|
const item = this.lockedItem;
|
|
7911
8071
|
const data = {
|
|
7912
8072
|
canvas: this,
|
|
7913
|
-
pointers: [],
|
|
7914
8073
|
deltaX: 0,
|
|
7915
8074
|
deltaY: 0,
|
|
7916
8075
|
item
|
|
@@ -7927,11 +8086,20 @@ class InteractiveCanvasComponent {
|
|
|
7927
8086
|
fixRotation() {
|
|
7928
8087
|
if (this.fullHeight <= 0)
|
|
7929
8088
|
return;
|
|
7930
|
-
this.rotation = ((this.rotation
|
|
7931
|
-
this.
|
|
7932
|
-
|
|
7933
|
-
this.cycles = this.
|
|
7934
|
-
? [
|
|
8089
|
+
this.rotation = overflow(Math.round(this.rotation * 100) / 100, -180, 180);
|
|
8090
|
+
this.basePan = (this.rotation / 360 - 1) * this.fullHeight
|
|
8091
|
+
+ this.canvasHeight * this.panOffset;
|
|
8092
|
+
this.cycles = this.infinite
|
|
8093
|
+
? [this.basePan - this.fullHeight, this.basePan, this.basePan + this.fullHeight] : [0];
|
|
8094
|
+
this.exclusions = (this.params.exclusions || []).flatMap(coords => {
|
|
8095
|
+
const x = (coords[2] + coords[0]) * .5 * this.ratio;
|
|
8096
|
+
const y = (coords[3] + coords[1]) * .5 * this.ratio;
|
|
8097
|
+
const width = Math.abs(coords[2] - coords[0]) * this.ratio;
|
|
8098
|
+
const height = Math.abs(coords[3] - coords[1]) * this.ratio;
|
|
8099
|
+
return this.cycles.map(cycle => {
|
|
8100
|
+
return new Rect(x, y + cycle, width, height);
|
|
8101
|
+
});
|
|
8102
|
+
});
|
|
7935
8103
|
this.items.forEach(item => {
|
|
7936
8104
|
item.calcShapes();
|
|
7937
8105
|
});
|
|
@@ -7957,19 +8125,25 @@ class InteractiveCanvasComponent {
|
|
|
7957
8125
|
return;
|
|
7958
8126
|
item.active = !item.active;
|
|
7959
8127
|
}
|
|
8128
|
+
this.hoveredIndex = selected;
|
|
7960
8129
|
}
|
|
7961
|
-
|
|
7962
|
-
if (!pointer || !this.
|
|
8130
|
+
toCanvasPoint(pointer) {
|
|
8131
|
+
if (!pointer || !this.canvas)
|
|
7963
8132
|
return null;
|
|
7964
|
-
const canvasRect = this.
|
|
7965
|
-
|
|
8133
|
+
const canvasRect = this.canvas?.getBoundingClientRect();
|
|
8134
|
+
return this.horizontal
|
|
7966
8135
|
? new Point(canvasRect.bottom - pointer.clientY, pointer.clientX - canvasRect.left)
|
|
7967
8136
|
: new Point(pointer.clientX - canvasRect.left, pointer.clientY - canvasRect.top);
|
|
8137
|
+
}
|
|
8138
|
+
getIndexUnderPointer(pointer) {
|
|
8139
|
+
const point = this.toCanvasPoint(pointer);
|
|
8140
|
+
if (!point || !this.items)
|
|
8141
|
+
return -1;
|
|
7968
8142
|
const length = this.items.length;
|
|
7969
8143
|
for (let ix = 0; ix < length; ix++) {
|
|
7970
8144
|
const item = this.items[ix];
|
|
7971
8145
|
if (item?.hit(point)) {
|
|
7972
|
-
return item.disabled ?
|
|
8146
|
+
return item.disabled ? -1 : ix;
|
|
7973
8147
|
}
|
|
7974
8148
|
}
|
|
7975
8149
|
return -1;
|
|
@@ -8005,17 +8179,15 @@ class InteractiveCanvasComponent {
|
|
|
8005
8179
|
}
|
|
8006
8180
|
async drawItems() {
|
|
8007
8181
|
const ctx = this.ctx;
|
|
8182
|
+
const lockedItem = this.lockedItem;
|
|
8008
8183
|
for (const item of this.items) {
|
|
8009
|
-
|
|
8010
|
-
|
|
8011
|
-
ctx.translate(shape.x, shape.y);
|
|
8012
|
-
ctx.lineWidth = 1;
|
|
8013
|
-
ctx.strokeStyle = "black";
|
|
8014
|
-
ctx.fillStyle = "white";
|
|
8015
|
-
await item.draw(ctx);
|
|
8016
|
-
ctx.restore();
|
|
8184
|
+
if (item !== lockedItem) {
|
|
8185
|
+
await this.drawItem(ctx, item);
|
|
8017
8186
|
}
|
|
8018
8187
|
}
|
|
8188
|
+
if (lockedItem) {
|
|
8189
|
+
await this.drawItem(ctx, lockedItem);
|
|
8190
|
+
}
|
|
8019
8191
|
if (!this.debug)
|
|
8020
8192
|
return;
|
|
8021
8193
|
ctx.lineWidth = 2;
|
|
@@ -8038,6 +8210,17 @@ class InteractiveCanvasComponent {
|
|
|
8038
8210
|
}
|
|
8039
8211
|
}
|
|
8040
8212
|
}
|
|
8213
|
+
async drawItem(ctx, item) {
|
|
8214
|
+
for (const shape of item.shapes) {
|
|
8215
|
+
ctx.save();
|
|
8216
|
+
ctx.translate(shape.x, shape.y);
|
|
8217
|
+
ctx.lineWidth = 1;
|
|
8218
|
+
ctx.strokeStyle = "black";
|
|
8219
|
+
ctx.fillStyle = "white";
|
|
8220
|
+
await item.draw(ctx);
|
|
8221
|
+
ctx.restore();
|
|
8222
|
+
}
|
|
8223
|
+
}
|
|
8041
8224
|
async draw() {
|
|
8042
8225
|
const ctx = this.ctx;
|
|
8043
8226
|
const canvas = ctx.canvas;
|
|
@@ -8063,30 +8246,29 @@ class InteractiveCanvasComponent {
|
|
|
8063
8246
|
}
|
|
8064
8247
|
ctx.restore();
|
|
8065
8248
|
}
|
|
8066
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: InteractiveCanvasComponent, deps: [{ token: i0.Renderer2 }, { token: UniversalService }
|
|
8067
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: InteractiveCanvasComponent, isStandalone: false, selector: "interactive-canvas", inputs: {
|
|
8249
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: InteractiveCanvasComponent, deps: [{ token: i0.Renderer2 }, { token: UniversalService }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
8250
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: InteractiveCanvasComponent, isStandalone: false, selector: "interactive-canvas", inputs: { infinite: "infinite", resizeMode: "resizeMode", params: "params", realWidth: "realWidth", realHeight: "realHeight", debug: "debug", horizontal: "horizontal", selectedIndex: "selectedIndex", panOffset: "panOffset", renderCtx: "renderCtx", beforeItems: "beforeItems", afterItems: "afterItems" }, outputs: { selectedIndexChange: "selectedIndexChange", onRotate: "onRotate", onItemPan: "onItemPan", onItemPanned: "onItemPanned", onPan: "onPan", onPanned: "onPanned" }, host: { listeners: { "window:touchend": "onTouchEnd($event)", "window:mouseup": "onMouseUp($event)" } }, queries: [{ propertyName: "itemList", predicate: InteractiveItemComponent }], viewQueries: [{ propertyName: "containerElem", first: true, predicate: ["containerElem"], descendants: true, static: true }, { propertyName: "canvasElem", first: true, predicate: ["canvasElem"], descendants: true, static: true }], usesOnChanges: true, ngImport: i0, template: "<div #containerElem\n [ngClass]=\"['interactive-canvas-container', horizontal ? 'horizontal' : 'vertical']\"\n (resize)=\"resize()\"\n (touchstart)=\"onTouchStart($event)\"\n (mousedown)=\"onMouseDown($event)\"\n (mousemove)=\"onMouseMove($event)\"\n (mouseleave)=\"onMouseLeave()\"\n (panend)=\"onPanEnd()\"\n (panmove)=\"onPanMove($event)\"\n (panstart)=\"onPanStart()\">\n <canvas #canvasElem class=\"interactive-canvas-element\"></canvas>\n</div>\n", styles: [".interactive-canvas-container{width:100%;height:100%;position:relative;display:flex;align-items:center;justify-content:center}.interactive-canvas-container .interactive-canvas-element{position:absolute;pointer-events:none}\n"], dependencies: [{ kind: "directive", type: i1$3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }] }); }
|
|
8068
8251
|
}
|
|
8069
8252
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: InteractiveCanvasComponent, decorators: [{
|
|
8070
8253
|
type: Component,
|
|
8071
8254
|
args: [{ standalone: false, selector: "interactive-canvas", template: "<div #containerElem\n [ngClass]=\"['interactive-canvas-container', horizontal ? 'horizontal' : 'vertical']\"\n (resize)=\"resize()\"\n (touchstart)=\"onTouchStart($event)\"\n (mousedown)=\"onMouseDown($event)\"\n (mousemove)=\"onMouseMove($event)\"\n (mouseleave)=\"onMouseLeave()\"\n (panend)=\"onPanEnd()\"\n (panmove)=\"onPanMove($event)\"\n (panstart)=\"onPanStart()\">\n <canvas #canvasElem class=\"interactive-canvas-element\"></canvas>\n</div>\n", styles: [".interactive-canvas-container{width:100%;height:100%;position:relative;display:flex;align-items:center;justify-content:center}.interactive-canvas-container .interactive-canvas-element{position:absolute;pointer-events:none}\n"] }]
|
|
8072
|
-
}], ctorParameters: () => [{ type: i0.Renderer2 }, { type: UniversalService },
|
|
8073
|
-
type: Inject,
|
|
8074
|
-
args: [ROOT_ELEMENT]
|
|
8075
|
-
}] }], propDecorators: { debug: [{
|
|
8076
|
-
type: Input
|
|
8077
|
-
}], horizontal: [{
|
|
8078
|
-
type: Input
|
|
8079
|
-
}], selectedIndex: [{
|
|
8255
|
+
}], ctorParameters: () => [{ type: i0.Renderer2 }, { type: UniversalService }], propDecorators: { infinite: [{
|
|
8080
8256
|
type: Input
|
|
8081
8257
|
}], resizeMode: [{
|
|
8082
8258
|
type: Input
|
|
8259
|
+
}], params: [{
|
|
8260
|
+
type: Input
|
|
8083
8261
|
}], realWidth: [{
|
|
8084
8262
|
type: Input
|
|
8085
8263
|
}], realHeight: [{
|
|
8086
8264
|
type: Input
|
|
8087
|
-
}],
|
|
8265
|
+
}], debug: [{
|
|
8088
8266
|
type: Input
|
|
8089
|
-
}],
|
|
8267
|
+
}], horizontal: [{
|
|
8268
|
+
type: Input
|
|
8269
|
+
}], selectedIndex: [{
|
|
8270
|
+
type: Input
|
|
8271
|
+
}], panOffset: [{
|
|
8090
8272
|
type: Input
|
|
8091
8273
|
}], renderCtx: [{
|
|
8092
8274
|
type: Input
|
|
@@ -8913,5 +9095,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImpo
|
|
|
8913
9095
|
* Generated bundle index. Do not edit.
|
|
8914
9096
|
*/
|
|
8915
9097
|
|
|
8916
|
-
export { API_SERVICE, APP_BASE_URL, AUTH_SERVICE, AclService, AjaxRequestHandler, ApiService, ArrayUtils, AsyncMethodBase, AsyncMethodDirective, AsyncMethodTargetDirective, AuthGuard, BASE_CONFIG, BUTTON_TYPE, BackgroundDirective, BaseDialogService, BaseHttpClient, BaseHttpService, BaseToasterService, BtnComponent, BtnDefaultComponent, CONFIG_SERVICE, CacheService, CanvasColor, CanvasUtils, ChipsComponent, ChunkPipe, Circle, CloseBtnComponent, ComponentLoaderDirective, ComponentLoaderService, ConfigService, DIALOG_SERVICE, DateUtils, DragDropEventPlugin, DropListComponent, DropdownBoxComponent, DropdownContentDirective, DropdownDirective, DropdownToggleDirective, DynamicTableComponent, DynamicTableTemplateDirective, ERROR_HANDLER, EXPRESS_REQUEST, EntriesPipe, ErrorHandlerService, EventsService, ExtraItemPropertiesPipe, FactoryDependencies, FakeModuleComponent, FileSystemEntry, FileUtils, FilterPipe, FindPipe, ForbiddenZone, FormatNumberPipe, FormatterService, GenericValue, GetOffsetPipe, GetTypePipe, GetValuePipe, GlobalTemplateDirective, GlobalTemplatePipe, GlobalTemplateService, GroupByPipe, ICON_MAP, ICON_SERVICE, ICON_TYPE, IConfiguration, IconComponent, IconDefaultComponent, IconDirective, IconService, IncludesPipe, Initializer, InteractiveCanvasComponent, InteractiveCircleComponent, InteractiveItemComponent, InteractiveRectComponent, IsTypePipe, JSONfn, JoinPipe, KeysPipe, LANGUAGE_SERVICE, LanguageService, LoaderUtils, LocalHttpService, MapPipe, MathUtils, MaxPipe, MinPipe, NgxTemplateOutletDirective, NgxUtilsModule, OPTIONS_TOKEN, ObjectType, ObjectUtils, ObservableUtils, OpenApiService, Oval, PROMISE_SERVICE, PaginationDirective, PaginationItemContext, PaginationItemDirective, PaginationMenuComponent, Point, PopPipe, PromiseService, RESIZE_DELAY, RESIZE_STRATEGY, ROOT_ELEMENT, Rect, ReducePipe, ReflectUtils, RemapPipe, ReplacePipe, ResizeEventPlugin, ResourceIfContext, ResourceIfDirective, ReversePipe, RoundPipe, SCRIPT_PARAMS, STATIC_SCHEMAS, SafeHtmlPipe, ScrollEventPlugin, SetUtils, ShiftPipe, SocketClient, SocketService, SplitPipe, StateService, StaticAuthService, StaticLanguageService, StickyClassDirective, StickyDirective, StorageMode, StorageService, StringUtils, TOASTER_SERVICE, TabsComponent, TabsItemDirective, TabsTemplateDirective, TimerUtils, TranslatePipe, TranslatedUrlSerializer, UniqueUtils, UniversalService, UnorderedListComponent, UnorderedListItemDirective, UnorderedListTemplateDirective, UploadComponent, ValuedPromise, ValuesPipe, cachedFactory, cancelablePromise, checkTransitions, computedPrevious, createTypedProvider, cssStyles, cssVariables, dotProduct, drawOval, drawRect, getComponentDef, getCssVariables, getRoot, hashCode, impatientPromise, isBrowser, isPoint,
|
|
9098
|
+
export { API_SERVICE, APP_BASE_URL, AUTH_SERVICE, AclService, AjaxRequestHandler, ApiService, ArrayUtils, AsyncMethodBase, AsyncMethodDirective, AsyncMethodTargetDirective, AuthGuard, BASE_CONFIG, BUTTON_TYPE, BackgroundDirective, BaseDialogService, BaseHttpClient, BaseHttpService, BaseToasterService, BtnComponent, BtnDefaultComponent, CONFIG_SERVICE, CacheService, CanvasColor, CanvasUtils, ChipsComponent, ChunkPipe, Circle, CloseBtnComponent, ComponentLoaderDirective, ComponentLoaderService, ConfigService, DIALOG_SERVICE, DateUtils, DragDropEventPlugin, DropListComponent, DropdownBoxComponent, DropdownContentDirective, DropdownDirective, DropdownToggleDirective, DynamicTableComponent, DynamicTableTemplateDirective, EPSILON, ERROR_HANDLER, EXPRESS_REQUEST, EntriesPipe, ErrorHandlerService, EventsService, ExtraItemPropertiesPipe, FactoryDependencies, FakeModuleComponent, FileSystemEntry, FileUtils, FilterPipe, FindPipe, ForbiddenZone, FormatNumberPipe, FormatterService, GenericValue, GetOffsetPipe, GetTypePipe, GetValuePipe, GlobalTemplateDirective, GlobalTemplatePipe, GlobalTemplateService, GroupByPipe, ICON_MAP, ICON_SERVICE, ICON_TYPE, IConfiguration, IconComponent, IconDefaultComponent, IconDirective, IconService, IncludesPipe, Initializer, InteractiveCanvasComponent, InteractiveCircleComponent, InteractiveItemComponent, InteractiveRectComponent, IsTypePipe, JSONfn, JoinPipe, KeysPipe, LANGUAGE_SERVICE, LanguageService, LoaderUtils, LocalHttpService, MapPipe, MathUtils, MaxPipe, MinPipe, NgxTemplateOutletDirective, NgxUtilsModule, OPTIONS_TOKEN, ObjectType, ObjectUtils, ObservableUtils, OpenApiService, Oval, PROMISE_SERVICE, PaginationDirective, PaginationItemContext, PaginationItemDirective, PaginationMenuComponent, Point, PopPipe, PromiseService, RESIZE_DELAY, RESIZE_STRATEGY, ROOT_ELEMENT, Rect, ReducePipe, ReflectUtils, RemapPipe, ReplacePipe, ResizeEventPlugin, ResourceIfContext, ResourceIfDirective, ReversePipe, RoundPipe, SCRIPT_PARAMS, STATIC_SCHEMAS, SafeHtmlPipe, ScrollEventPlugin, SetUtils, ShiftPipe, SocketClient, SocketService, SplitPipe, StateService, StaticAuthService, StaticLanguageService, StickyClassDirective, StickyDirective, StorageMode, StorageService, StringUtils, TOASTER_SERVICE, TabsComponent, TabsItemDirective, TabsTemplateDirective, TimerUtils, TranslatePipe, TranslatedUrlSerializer, UniqueUtils, UniversalService, UnorderedListComponent, UnorderedListItemDirective, UnorderedListTemplateDirective, UploadComponent, ValuedPromise, ValuesPipe, addPts, cachedFactory, cancelablePromise, checkTransitions, clamp, computedPrevious, createTypedProvider, cssStyles, cssVariables, distance, distanceSq, dividePts, dotProduct, drawOval, drawRect, ensurePoint, getComponentDef, getCssVariables, getRoot, gjkDistance, gjkIntersection, hashCode, impatientPromise, isBrowser, isPoint, lengthOfPt, lerpPts, multiplyPts, negatePt, normalizePt, normalizeRange, overflow, parseSelector, perpendicular, provideEntryComponents, provideWithOptions, rotateDeg, rotateRad, selectorMatchesList, subPts, switchClass, toDegrees, toRadians, tripleProduct };
|
|
8917
9099
|
//# sourceMappingURL=stemy-ngx-utils.mjs.map
|