@stemy/ngx-utils 19.7.2 → 19.7.4

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.
@@ -1895,36 +1895,64 @@ const MAX_ITERS = 40;
1895
1895
  // GJK distance (robust)
1896
1896
  // =========================
1897
1897
  function gjkDistance(A, B) {
1898
- let intersection = gjkIntersection(A, B);
1899
- if (intersection.hit)
1900
- return { distance: 0 };
1898
+ // 1) Quick overlap
1899
+ const inter = gjkIntersection(A, B);
1900
+ if (inter.hit) {
1901
+ // Pass through pa/pb
1902
+ return { distance: 0, pa: inter.pa ?? null, pb: inter.pb ?? null };
1903
+ }
1901
1904
  const ca = A.center;
1902
1905
  const cb = B.center;
1906
+ // 2) Bisection along the center-line to find the first hit pose
1903
1907
  let s = 0;
1904
1908
  let e = 1;
1905
- let center = ca;
1906
1909
  let iters = 0;
1907
- while (e - s > EPSILON) {
1910
+ // Keep the best "hit" snapshot and its center so we can map witnesses back
1911
+ let hitSnap = null;
1912
+ let hitCenter = ca;
1913
+ while ((e - s) > EPSILON && iters < MAX_ITERS) {
1908
1914
  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;
1915
+ const t = (e + s) * 0.5;
1916
+ // Assumes A.move(newCenter) returns a NEW shape whose center is exactly this point
1917
+ const aMoved = A.move(lerpPts(ca, cb, t));
1918
+ const test = gjkIntersection(aMoved, B);
1913
1919
  if (test.hit) {
1914
- intersection = test;
1915
- e = t;
1916
- if (iters >= MAX_ITERS)
1917
- break;
1920
+ hitSnap = test;
1921
+ hitCenter = aMoved.center;
1922
+ e = t; // shrink toward contact
1918
1923
  }
1919
1924
  else {
1920
- s = t;
1925
+ s = t; // still separated
1921
1926
  }
1922
1927
  }
1923
- const result = distance(ca, center);
1928
+ // 3) Make sure we end with a hit snapshot (in case we stopped on iteration cap)
1929
+ if (!hitSnap) {
1930
+ const aMoved = A.move(lerpPts(ca, cb, e));
1931
+ const test = gjkIntersection(aMoved, B);
1932
+ if (test.hit) {
1933
+ hitSnap = test;
1934
+ hitCenter = aMoved.center;
1935
+ }
1936
+ else {
1937
+ // Extremely degenerate: no hit even at e ~ 1 (shouldn't happen for non-degenerate shapes).
1938
+ // Fall back to center-line direction as a last resort.
1939
+ const dir = normalizePt(subPts(cb, ca));
1940
+ const pa0 = A.support(dir);
1941
+ const pb0 = B.support(negatePt(dir));
1942
+ return { distance: distance(pa0, pb0), pa: pa0, pb: pb0 };
1943
+ }
1944
+ }
1945
+ // 4) Map witnesses back to the original A pose
1946
+ // (We moved A by (hitCenter - ca); to undo, offset A's witness by (ca - hitCenter))
1947
+ const offset = subPts(ca, hitCenter);
1948
+ const pa0 = addPts(hitSnap.pa, offset);
1949
+ const pb0 = hitSnap.pb;
1950
+ // 5) True geometric separation is the distance between these boundary points
1951
+ const d = distance(pa0, pb0);
1924
1952
  return {
1925
- distance: result,
1926
- pa: result > 0 ? addPts(intersection.pa, subPts(ca, center)) : null,
1927
- pb: result > 0 ? intersection.pb : null
1953
+ distance: d,
1954
+ pa: d > 0 ? pa0 : null,
1955
+ pb: d > 0 ? pb0 : null
1928
1956
  };
1929
1957
  }
1930
1958
  // =========================
@@ -2010,20 +2038,20 @@ function doSimplexBoolean(simplex, d) {
2010
2038
  const A = last, B = simplex[simplex.length - 2], C = simplex[simplex.length - 3];
2011
2039
  const AB = { x: B.p.x - A.p.x, y: B.p.y - A.p.y };
2012
2040
  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);
2041
+ const perpAB = tripleProduct(AC, AB, AB);
2042
+ const perpAC = tripleProduct(AB, AC, AC);
2015
2043
  // If origin is outside AB region
2016
- if (ABperp.x * AO.x + ABperp.y * AO.y > 0) {
2044
+ if (perpAB.x * AO.x + perpAB.y * AO.y > 0) {
2017
2045
  simplex.splice(simplex.length - 3, 1);
2018
- d.x = ABperp.x;
2019
- d.y = ABperp.y;
2046
+ d.x = perpAB.x;
2047
+ d.y = perpAB.y;
2020
2048
  return { hit: false };
2021
2049
  }
2022
2050
  // If origin is outside AC region
2023
- if (ACperp.x * AO.x + ACperp.y * AO.y > 0) {
2051
+ if (perpAC.x * AO.x + perpAC.y * AO.y > 0) {
2024
2052
  simplex.splice(simplex.length - 2, 1);
2025
- d.x = ACperp.x;
2026
- d.y = ACperp.y;
2053
+ d.x = perpAC.x;
2054
+ d.y = perpAC.y;
2027
2055
  return { hit: false };
2028
2056
  }
2029
2057
  // Otherwise the origin is inside the triangle → overlap
@@ -7851,8 +7879,8 @@ class InteractiveItemComponent {
7851
7879
  }
7852
7880
  isValidByParams() {
7853
7881
  return !this.shapes.some(shape => {
7854
- return this.canvas.exclusions.some(ex => {
7855
- return shape.distance(ex) < 1;
7882
+ return this.canvas.excludedAreas.some(ex => {
7883
+ return shape.intersects(ex);
7856
7884
  });
7857
7885
  });
7858
7886
  }
@@ -7949,7 +7977,7 @@ class InteractiveCanvasComponent {
7949
7977
  this.rotation = 0;
7950
7978
  this.basePan = 0;
7951
7979
  this.cycles = [0];
7952
- this.exclusions = [];
7980
+ this.excludedAreas = [];
7953
7981
  this.touched = false;
7954
7982
  this.panStartRotation = 0;
7955
7983
  this.panStartPos = Point.Zero;
@@ -8091,11 +8119,11 @@ class InteractiveCanvasComponent {
8091
8119
  + this.canvasHeight * this.panOffset;
8092
8120
  this.cycles = this.infinite
8093
8121
  ? [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;
8122
+ this.excludedAreas = (this.params.excludedAreas || []).flatMap(coords => {
8123
+ const x = coords.x * this.ratio;
8124
+ const y = coords.y * this.ratio;
8125
+ const width = coords.width * this.ratio;
8126
+ const height = coords.height * this.ratio;
8099
8127
  return this.cycles.map(cycle => {
8100
8128
  return new Rect(x, y + cycle, width, height);
8101
8129
  });