@qe-libs/qeviz 0.1.0 → 0.2.0
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/README.md +16 -0
- package/dist/qeviz.es.js +624 -25
- package/dist/qeviz.es.js.map +1 -1
- package/dist/qeviz.umd.js +624 -25
- package/dist/qeviz.umd.js.map +1 -1
- package/package.json +1 -1
package/dist/qeviz.umd.js
CHANGED
|
@@ -164,7 +164,7 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
164
164
|
}
|
|
165
165
|
});
|
|
166
166
|
__privateSet(this, _names2, names);
|
|
167
|
-
if (rowNames && rowNames.length !== this[0].length) {
|
|
167
|
+
if (rowNames && this[0] && rowNames.length !== this[0].length) {
|
|
168
168
|
throw new Error("Row names length must match the number of rows in the DataFrame.");
|
|
169
169
|
}
|
|
170
170
|
if (rowNames && rowNames.length > 0) {
|
|
@@ -228,6 +228,9 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
228
228
|
rows(...wh) {
|
|
229
229
|
let selectors, selectedRows = [], selectedRowNames = [];
|
|
230
230
|
if (wh.length === 0) {
|
|
231
|
+
if (!this[0] || this.length === 0) {
|
|
232
|
+
return [];
|
|
233
|
+
}
|
|
231
234
|
const numRows = this[0].length;
|
|
232
235
|
selectedRows = Array.from({ length: numRows }, (_, i) => this.getRowByIndex(i));
|
|
233
236
|
selectedRowNames = __privateGet(this, _rowNames2);
|
|
@@ -286,7 +289,7 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
286
289
|
return __privateGet(this, _rowNames2);
|
|
287
290
|
}
|
|
288
291
|
get nrow() {
|
|
289
|
-
return this[0].length;
|
|
292
|
+
return this[0] ? this[0].length : 0;
|
|
290
293
|
}
|
|
291
294
|
get ncol() {
|
|
292
295
|
return this.length;
|
|
@@ -353,6 +356,12 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
353
356
|
set groups(v) {
|
|
354
357
|
this._groups = v;
|
|
355
358
|
}
|
|
359
|
+
get trajectories() {
|
|
360
|
+
return this._trajectories;
|
|
361
|
+
}
|
|
362
|
+
set trajectories(v) {
|
|
363
|
+
this._trajectories = v;
|
|
364
|
+
}
|
|
356
365
|
get dimensions() {
|
|
357
366
|
return this._dimensions;
|
|
358
367
|
}
|
|
@@ -465,7 +474,7 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
465
474
|
*/
|
|
466
475
|
setModelData(data) {
|
|
467
476
|
var _a, _b, _c, _d;
|
|
468
|
-
const frameKeys = ["nodes", "edges", "points", "groups", "confidence", "outlier"];
|
|
477
|
+
const frameKeys = ["nodes", "edges", "points", "groups", "confidence", "outlier", "trajectories"];
|
|
469
478
|
frameKeys.forEach((name) => {
|
|
470
479
|
var _a2;
|
|
471
480
|
const raw = data[name];
|
|
@@ -474,7 +483,7 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
474
483
|
let idCol;
|
|
475
484
|
if (name === "nodes") {
|
|
476
485
|
idCol = data.node_id_col ?? colNames.find((c) => (types[c] ?? "character") === "character");
|
|
477
|
-
} else if (name === "edges" || name === "points") {
|
|
486
|
+
} else if (name === "edges" || name === "points" || name === "trajectories") {
|
|
478
487
|
idCol = data.id_col ?? colNames.find((c) => (types[c] ?? "character") === "character");
|
|
479
488
|
} else {
|
|
480
489
|
idCol = data.group_col ?? colNames.find((c) => (types[c] ?? "character") === "character");
|
|
@@ -544,8 +553,12 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
544
553
|
__name(_QEVisualElement, "QEVisualElement");
|
|
545
554
|
_QEVisualElement.tagName = "qe-visual";
|
|
546
555
|
_QEVisualElement.styles = `
|
|
547
|
-
:host { display:
|
|
556
|
+
:host { display: block; width: 100%; }
|
|
548
557
|
:host(.hidden) { display: none !important; }
|
|
558
|
+
/* The output wrapper and the slotted graph fill the host so the plot
|
|
559
|
+
stretches to its container instead of collapsing to intrinsic size. */
|
|
560
|
+
div { display: block; width: 100%; height: 100%; }
|
|
561
|
+
::slotted(qe-graph) { display: block; width: 100%; height: 100%; }
|
|
549
562
|
`;
|
|
550
563
|
let QEVisualElement = _QEVisualElement;
|
|
551
564
|
customElements.define(QEVisualElement.tagName, QEVisualElement);
|
|
@@ -816,7 +829,9 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
816
829
|
__name(slopeIntercept, "slopeIntercept");
|
|
817
830
|
Line2.slopeIntercept = slopeIntercept;
|
|
818
831
|
function point_along(line, distance = 0) {
|
|
819
|
-
|
|
832
|
+
const ratio = distance;
|
|
833
|
+
const Xt = (1 - ratio) * line[0][0] + ratio * line[1][0];
|
|
834
|
+
const Yt = (1 - ratio) * line[0][1] + ratio * line[1][1];
|
|
820
835
|
return [Xt, Yt];
|
|
821
836
|
}
|
|
822
837
|
__name(point_along, "point_along");
|
|
@@ -1663,6 +1678,7 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
1663
1678
|
this.points = /* @__PURE__ */ new Map();
|
|
1664
1679
|
this.nodes = /* @__PURE__ */ new Map();
|
|
1665
1680
|
this.edges = /* @__PURE__ */ new Map();
|
|
1681
|
+
this.trajectories = /* @__PURE__ */ new Map();
|
|
1666
1682
|
this.labels = /* @__PURE__ */ new Map();
|
|
1667
1683
|
this.transform = new Transform();
|
|
1668
1684
|
this.scale_x = 1;
|
|
@@ -1705,6 +1721,7 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
1705
1721
|
{ name: "edgenodes", layers: [], tabindex: 0 }
|
|
1706
1722
|
]
|
|
1707
1723
|
},
|
|
1724
|
+
{ name: "trajectories", layers: [], tabindex: 0 },
|
|
1708
1725
|
{ name: "points", layers: [], tabindex: 0 },
|
|
1709
1726
|
{ name: "labels", layers: [], tabindex: 0 }
|
|
1710
1727
|
] }
|
|
@@ -1830,9 +1847,13 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
1830
1847
|
if (search_layers.length === 0) {
|
|
1831
1848
|
this.extremes = new Extremes(1, 1);
|
|
1832
1849
|
} else {
|
|
1833
|
-
const
|
|
1834
|
-
|
|
1835
|
-
|
|
1850
|
+
const content_max = Math.max(
|
|
1851
|
+
...search_layers.map((l) => Math.max(
|
|
1852
|
+
...[...l.objects.values()].map((o) => Math.max(Math.abs(o.x), Math.abs(o.y)))
|
|
1853
|
+
)).filter((a) => Math.abs(a) !== Infinity)
|
|
1854
|
+
);
|
|
1855
|
+
const found_max = (Number.isFinite(content_max) && content_max > 0 ? content_max : 1) * 1.3;
|
|
1856
|
+
this.extremes = new Extremes(found_max, found_max);
|
|
1836
1857
|
}
|
|
1837
1858
|
}
|
|
1838
1859
|
base_point_size(scale = 8) {
|
|
@@ -1872,6 +1893,251 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
1872
1893
|
}
|
|
1873
1894
|
`;
|
|
1874
1895
|
let DrawableElement = _DrawableElement;
|
|
1896
|
+
const TRAJECTORY_ZERO_EPS = 1e-8;
|
|
1897
|
+
function nearOriginFilter(pts, eps = TRAJECTORY_ZERO_EPS) {
|
|
1898
|
+
return pts.filter((p) => Math.abs(p.x) > eps || Math.abs(p.y) > eps);
|
|
1899
|
+
}
|
|
1900
|
+
__name(nearOriginFilter, "nearOriginFilter");
|
|
1901
|
+
function normalizedT(n) {
|
|
1902
|
+
if (n <= 0) return [];
|
|
1903
|
+
if (n === 1) return [0];
|
|
1904
|
+
const t = new Array(n);
|
|
1905
|
+
for (let i = 0; i < n; i++) t[i] = i / (n - 1);
|
|
1906
|
+
return t;
|
|
1907
|
+
}
|
|
1908
|
+
__name(normalizedT, "normalizedT");
|
|
1909
|
+
function design(t, degree) {
|
|
1910
|
+
return t.map((ti) => {
|
|
1911
|
+
const row = new Array(degree + 1);
|
|
1912
|
+
let p = 1;
|
|
1913
|
+
for (let k = 0; k <= degree; k++) {
|
|
1914
|
+
row[k] = p;
|
|
1915
|
+
p *= ti;
|
|
1916
|
+
}
|
|
1917
|
+
return row;
|
|
1918
|
+
});
|
|
1919
|
+
}
|
|
1920
|
+
__name(design, "design");
|
|
1921
|
+
function qr(A) {
|
|
1922
|
+
const n = A.length;
|
|
1923
|
+
const m = A[0].length;
|
|
1924
|
+
const Q = A.map((row) => row.slice());
|
|
1925
|
+
const R = Array.from({ length: m }, () => new Array(m).fill(0));
|
|
1926
|
+
for (let j = 0; j < m; j++) {
|
|
1927
|
+
let norm = 0;
|
|
1928
|
+
for (let i = 0; i < n; i++) norm += Q[i][j] * Q[i][j];
|
|
1929
|
+
norm = Math.sqrt(norm);
|
|
1930
|
+
R[j][j] = norm;
|
|
1931
|
+
if (norm > 1e-300) {
|
|
1932
|
+
for (let i = 0; i < n; i++) Q[i][j] /= norm;
|
|
1933
|
+
}
|
|
1934
|
+
for (let k = j + 1; k < m; k++) {
|
|
1935
|
+
let dot = 0;
|
|
1936
|
+
for (let i = 0; i < n; i++) dot += Q[i][j] * Q[i][k];
|
|
1937
|
+
R[j][k] = dot;
|
|
1938
|
+
for (let i = 0; i < n; i++) Q[i][k] -= dot * Q[i][j];
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
return { Q, R };
|
|
1942
|
+
}
|
|
1943
|
+
__name(qr, "qr");
|
|
1944
|
+
function qtY(Q, y) {
|
|
1945
|
+
const n = Q.length;
|
|
1946
|
+
const m = Q[0].length;
|
|
1947
|
+
const out = new Array(m).fill(0);
|
|
1948
|
+
for (let j = 0; j < m; j++) {
|
|
1949
|
+
let s = 0;
|
|
1950
|
+
for (let i = 0; i < n; i++) s += Q[i][j] * y[i];
|
|
1951
|
+
out[j] = s;
|
|
1952
|
+
}
|
|
1953
|
+
return out;
|
|
1954
|
+
}
|
|
1955
|
+
__name(qtY, "qtY");
|
|
1956
|
+
function backSolve(R, b) {
|
|
1957
|
+
const m = b.length;
|
|
1958
|
+
const c = new Array(m).fill(0);
|
|
1959
|
+
for (let i = m - 1; i >= 0; i--) {
|
|
1960
|
+
let s = b[i];
|
|
1961
|
+
for (let j = i + 1; j < m; j++) s -= R[i][j] * c[j];
|
|
1962
|
+
c[i] = Math.abs(R[i][i]) > 1e-12 ? s / R[i][i] : 0;
|
|
1963
|
+
}
|
|
1964
|
+
return c;
|
|
1965
|
+
}
|
|
1966
|
+
__name(backSolve, "backSolve");
|
|
1967
|
+
function leverages(Q) {
|
|
1968
|
+
return Q.map((row) => {
|
|
1969
|
+
let s = 0;
|
|
1970
|
+
for (let j = 0; j < row.length; j++) s += row[j] * row[j];
|
|
1971
|
+
return s;
|
|
1972
|
+
});
|
|
1973
|
+
}
|
|
1974
|
+
__name(leverages, "leverages");
|
|
1975
|
+
function fitCoeffs(t, y, degree) {
|
|
1976
|
+
const X = design(t, degree);
|
|
1977
|
+
const { Q, R } = qr(X);
|
|
1978
|
+
return backSolve(R, qtY(Q, y));
|
|
1979
|
+
}
|
|
1980
|
+
__name(fitCoeffs, "fitCoeffs");
|
|
1981
|
+
function evalPoly(coeffs, t) {
|
|
1982
|
+
let v = 0;
|
|
1983
|
+
let p = 1;
|
|
1984
|
+
for (let k = 0; k < coeffs.length; k++) {
|
|
1985
|
+
v += coeffs[k] * p;
|
|
1986
|
+
p *= t;
|
|
1987
|
+
}
|
|
1988
|
+
return v;
|
|
1989
|
+
}
|
|
1990
|
+
__name(evalPoly, "evalPoly");
|
|
1991
|
+
function loocv2d(t, x, y, degree) {
|
|
1992
|
+
const X = design(t, degree);
|
|
1993
|
+
const { Q, R } = qr(X);
|
|
1994
|
+
const h = leverages(Q);
|
|
1995
|
+
const cx = backSolve(R, qtY(Q, x));
|
|
1996
|
+
const cy = backSolve(R, qtY(Q, y));
|
|
1997
|
+
let press = 0;
|
|
1998
|
+
for (let i = 0; i < t.length; i++) {
|
|
1999
|
+
const denom = 1 - h[i];
|
|
2000
|
+
if (denom <= 0) return Infinity;
|
|
2001
|
+
const ex = x[i] - evalPoly(cx, t[i]);
|
|
2002
|
+
const ey = y[i] - evalPoly(cy, t[i]);
|
|
2003
|
+
press += (ex / denom) ** 2 + (ey / denom) ** 2;
|
|
2004
|
+
}
|
|
2005
|
+
return press;
|
|
2006
|
+
}
|
|
2007
|
+
__name(loocv2d, "loocv2d");
|
|
2008
|
+
function selectDegree(fitPts, maxDegree, mode) {
|
|
2009
|
+
const n = fitPts.length;
|
|
2010
|
+
const cap = Math.min(Math.max(1, Math.floor(maxDegree)), Math.max(1, n - 1));
|
|
2011
|
+
if (mode === "fixed" || n < 3) return cap;
|
|
2012
|
+
const t = normalizedT(n);
|
|
2013
|
+
const x = fitPts.map((p) => p.x);
|
|
2014
|
+
const y = fitPts.map((p) => p.y);
|
|
2015
|
+
let best = 1;
|
|
2016
|
+
let bestVal = Infinity;
|
|
2017
|
+
for (let deg = 1; deg <= cap; deg++) {
|
|
2018
|
+
const v = loocv2d(t, x, y, deg);
|
|
2019
|
+
if (v < bestVal) {
|
|
2020
|
+
bestVal = v;
|
|
2021
|
+
best = deg;
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
return best;
|
|
2025
|
+
}
|
|
2026
|
+
__name(selectDegree, "selectDegree");
|
|
2027
|
+
function fitTrajectory(activePts, opts) {
|
|
2028
|
+
const fitPts = nearOriginFilter(activePts);
|
|
2029
|
+
if (fitPts.length < 3) return null;
|
|
2030
|
+
const selBasis = opts.selectionSet ? nearOriginFilter(opts.selectionSet) : fitPts;
|
|
2031
|
+
const basis = selBasis.length >= 3 ? selBasis : fitPts;
|
|
2032
|
+
let degree = selectDegree(basis, opts.maxDegree, opts.degreeSelection);
|
|
2033
|
+
degree = Math.min(degree, Math.max(1, fitPts.length - 1));
|
|
2034
|
+
const t = normalizedT(fitPts.length);
|
|
2035
|
+
const coeffsX = fitCoeffs(t, fitPts.map((p) => p.x), degree);
|
|
2036
|
+
const coeffsY = fitCoeffs(t, fitPts.map((p) => p.y), degree);
|
|
2037
|
+
return { degree, coeffsX, coeffsY };
|
|
2038
|
+
}
|
|
2039
|
+
__name(fitTrajectory, "fitTrajectory");
|
|
2040
|
+
function sampleCurve(fit, steps = 100) {
|
|
2041
|
+
const out = [];
|
|
2042
|
+
for (let s = 0; s <= steps; s++) {
|
|
2043
|
+
const t = s / steps;
|
|
2044
|
+
out.push({ x: evalPoly(fit.coeffsX, t), y: evalPoly(fit.coeffsY, t) });
|
|
2045
|
+
}
|
|
2046
|
+
return out;
|
|
2047
|
+
}
|
|
2048
|
+
__name(sampleCurve, "sampleCurve");
|
|
2049
|
+
const TRAJECTORY_CURVE_STEPS = 100;
|
|
2050
|
+
const _Trajectory = class _Trajectory extends PlottedObject {
|
|
2051
|
+
constructor(options = {}) {
|
|
2052
|
+
const defaultColor = options.color instanceof Color ? options.color : new Color(typeof options.color === "string" ? options.color : "#000000");
|
|
2053
|
+
super([], {
|
|
2054
|
+
ID: options.ID || `traj-${Math.random().toString(36).substring(2, 9)}`,
|
|
2055
|
+
size: options.size || 1,
|
|
2056
|
+
color: defaultColor,
|
|
2057
|
+
highlight: null,
|
|
2058
|
+
active: options.active || false,
|
|
2059
|
+
meta: options.meta || {},
|
|
2060
|
+
...options
|
|
2061
|
+
});
|
|
2062
|
+
this._typeName = "Trajectory";
|
|
2063
|
+
this.trajPoints = [];
|
|
2064
|
+
this.smooth = "none";
|
|
2065
|
+
this.maxDegree = 3;
|
|
2066
|
+
this.degreeSelection = "loocv_2d";
|
|
2067
|
+
this.selectedDegree = -1;
|
|
2068
|
+
this.polyCoeffsX = [];
|
|
2069
|
+
this.polyCoeffsY = [];
|
|
2070
|
+
this.currentTime = Infinity;
|
|
2071
|
+
this.showPoints = true;
|
|
2072
|
+
this.showTips = true;
|
|
2073
|
+
this.lineStyle = "uniform";
|
|
2074
|
+
this.strokeWidth = 2;
|
|
2075
|
+
this.strokeWidthRange = [1, 6];
|
|
2076
|
+
this.opacityRange = [0.3, 1];
|
|
2077
|
+
this.smooth = options.smooth || "none";
|
|
2078
|
+
this.maxDegree = options.maxDegree !== void 0 ? options.maxDegree : 3;
|
|
2079
|
+
this.degreeSelection = options.degreeSelection || "loocv_2d";
|
|
2080
|
+
this.polyCoeffsX = options.polyCoeffsX || [];
|
|
2081
|
+
this.polyCoeffsY = options.polyCoeffsY || [];
|
|
2082
|
+
this.currentTime = options.currentTime !== void 0 ? options.currentTime : Infinity;
|
|
2083
|
+
this.showPoints = options.showPoints !== void 0 ? options.showPoints : true;
|
|
2084
|
+
this.showTips = options.showTips !== void 0 ? options.showTips : true;
|
|
2085
|
+
this.lineStyle = options.lineStyle || "uniform";
|
|
2086
|
+
this.strokeWidth = options.strokeWidth || 2;
|
|
2087
|
+
this.strokeWidthRange = options.strokeWidthRange || [1, 6];
|
|
2088
|
+
this.opacityRange = options.opacityRange || [0.3, 1];
|
|
2089
|
+
if (options.points && Array.isArray(options.points)) {
|
|
2090
|
+
this.trajPoints = options.points.map((p) => {
|
|
2091
|
+
if (p instanceof Point) return p;
|
|
2092
|
+
const pt = new Point({ dimensions: [p.x, p.y], meta: { time: p.time, label: p.label } });
|
|
2093
|
+
pt.x = p.x;
|
|
2094
|
+
pt.y = p.y;
|
|
2095
|
+
return pt;
|
|
2096
|
+
});
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
get activePoints() {
|
|
2100
|
+
if (this.currentTime === Infinity) {
|
|
2101
|
+
return this.trajPoints;
|
|
2102
|
+
}
|
|
2103
|
+
return this.trajPoints.filter((p) => {
|
|
2104
|
+
const t = p.meta && typeof p.meta.time === "number" ? p.meta.time : 0;
|
|
2105
|
+
return t <= this.currentTime;
|
|
2106
|
+
});
|
|
2107
|
+
}
|
|
2108
|
+
get path() {
|
|
2109
|
+
return this.pathPoints.reduce((acc, pt, idx) => {
|
|
2110
|
+
return idx === 0 ? `M${pt.x},${pt.y}` : `${acc} L${pt.x},${pt.y}`;
|
|
2111
|
+
}, "");
|
|
2112
|
+
}
|
|
2113
|
+
get pathPoints() {
|
|
2114
|
+
const pts = this.activePoints;
|
|
2115
|
+
if (pts.length < 2) return [];
|
|
2116
|
+
if (this.smooth === "poly") {
|
|
2117
|
+
if (this.polyCoeffsX.length > 0 && this.polyCoeffsY.length > 0) {
|
|
2118
|
+
return sampleCurve(
|
|
2119
|
+
{ degree: this.polyCoeffsX.length - 1, coeffsX: this.polyCoeffsX, coeffsY: this.polyCoeffsY },
|
|
2120
|
+
TRAJECTORY_CURVE_STEPS
|
|
2121
|
+
);
|
|
2122
|
+
}
|
|
2123
|
+
const fit = fitTrajectory(
|
|
2124
|
+
pts.map((p) => ({ x: p.x, y: p.y })),
|
|
2125
|
+
{
|
|
2126
|
+
maxDegree: this.maxDegree,
|
|
2127
|
+
degreeSelection: this.degreeSelection,
|
|
2128
|
+
selectionSet: this.trajPoints.map((p) => ({ x: p.x, y: p.y }))
|
|
2129
|
+
}
|
|
2130
|
+
);
|
|
2131
|
+
if (fit) {
|
|
2132
|
+
this.selectedDegree = fit.degree;
|
|
2133
|
+
return sampleCurve(fit, TRAJECTORY_CURVE_STEPS);
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
return pts.map((pt) => ({ x: pt.x, y: pt.y }));
|
|
2137
|
+
}
|
|
2138
|
+
};
|
|
2139
|
+
__name(_Trajectory, "Trajectory");
|
|
2140
|
+
let Trajectory = _Trajectory;
|
|
1875
2141
|
const zoom_level = 1;
|
|
1876
2142
|
const _Edge = class _Edge extends Line {
|
|
1877
2143
|
constructor(data, from, to, weight = 0) {
|
|
@@ -1919,7 +2185,7 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
1919
2185
|
}
|
|
1920
2186
|
get path() {
|
|
1921
2187
|
let _this = __privateGet(this, _edge);
|
|
1922
|
-
let _mid = _this.from_point, head = Tools.Geometry.Line.point_along([[_this.to.x, _this.to.y], _mid], 0.
|
|
2188
|
+
let _mid = _this.from_point, head = Tools.Geometry.Line.point_along([[_this.to.x, _this.to.y], _mid], 0.2), nock = Tools.Geometry.Line.point_along([[_this.to.x, _this.to.y], _mid], 0.35), mid = Tools.Geometry.Line.midpoint(head, nock), rot_mid = Tools.Geometry.Line.rotate(_this.line, mid), rot_nock = Tools.Geometry.Line.rotate(_this.line, nock), mid_int0 = Tools.Geometry.Line.intersection([_mid, _this.base_pts[0]], rot_mid), mid_int1 = Tools.Geometry.Line.intersection([_mid, _this.base_pts[1]], rot_mid), nock_int0 = Tools.Geometry.Line.intersection([_mid, _this.base_pts[0]], rot_nock), nock_int1 = Tools.Geometry.Line.intersection([_mid, _this.base_pts[1]], rot_nock);
|
|
1923
2189
|
let p = `M${head.join(" ")} ${mid_int0.join(" ")} ${nock_int0.join(" ")} ${mid.join(" ")} ${nock_int1.join(" ")} ${mid_int1.join(" ")}z`;
|
|
1924
2190
|
return p;
|
|
1925
2191
|
}
|
|
@@ -2204,11 +2470,22 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
2204
2470
|
const extent = this.graph.__data__.extremes.x_max;
|
|
2205
2471
|
return (extent > 0 ? extent : 1) * 6e-3 * this.__data__.size;
|
|
2206
2472
|
}
|
|
2473
|
+
/** Optional halo stroke (e.g. white outline on unit points to separate
|
|
2474
|
+
* overlapping dots in dense clusters). Width scales with the dot so it holds
|
|
2475
|
+
* up at any zoom. No-op unless the object requests it via meta.stroke. */
|
|
2476
|
+
applyStroke(el) {
|
|
2477
|
+
var _a;
|
|
2478
|
+
const stroke = (_a = this.__data__.meta) == null ? void 0 : _a.stroke;
|
|
2479
|
+
if (!stroke) return;
|
|
2480
|
+
el.setAttribute("stroke", stroke);
|
|
2481
|
+
el.setAttribute("stroke-width", (this.computeRadius() * 0.22).toString());
|
|
2482
|
+
}
|
|
2207
2483
|
create() {
|
|
2208
2484
|
const tag = this.isSquare ? "rect" : "circle";
|
|
2209
2485
|
const el = document.createElementNS("http://www.w3.org/2000/svg", tag);
|
|
2210
2486
|
el.setAttribute("data-point-id", this.__data__.ID);
|
|
2211
2487
|
el.setAttribute("fill", this.__data__.color.toString());
|
|
2488
|
+
this.applyStroke(el);
|
|
2212
2489
|
const r = this.computeRadius();
|
|
2213
2490
|
if (this.isSquare) {
|
|
2214
2491
|
el.setAttribute("x", (this.x - r).toString());
|
|
@@ -2246,6 +2523,7 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
2246
2523
|
}
|
|
2247
2524
|
const r = this.computeRadius();
|
|
2248
2525
|
this.svg_element.setAttribute("fill", this.__data__.color.toString());
|
|
2526
|
+
this.applyStroke(this.svg_element);
|
|
2249
2527
|
if (this.isSquare) {
|
|
2250
2528
|
this.svg_element.setAttribute("x", (this.x - r).toString());
|
|
2251
2529
|
this.svg_element.setAttribute("y", (this.y - r).toString());
|
|
@@ -2264,6 +2542,141 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
2264
2542
|
};
|
|
2265
2543
|
__name(_QEPoint, "QEPoint");
|
|
2266
2544
|
let QEPoint = _QEPoint;
|
|
2545
|
+
const _QETrajectory = class _QETrajectory extends Drawable {
|
|
2546
|
+
constructor(__data__, parent, graph, __children__ = new QEArray()) {
|
|
2547
|
+
super(__data__, parent, graph, __children__);
|
|
2548
|
+
this.__data__ = __data__;
|
|
2549
|
+
this.parent = parent;
|
|
2550
|
+
this.graph = graph;
|
|
2551
|
+
this.__children__ = __children__;
|
|
2552
|
+
this.__name__ = "trajectory";
|
|
2553
|
+
this._path_el = null;
|
|
2554
|
+
this._segments_group = null;
|
|
2555
|
+
this._tip_el = null;
|
|
2556
|
+
this._points_group = null;
|
|
2557
|
+
this.svg_element = this.create();
|
|
2558
|
+
}
|
|
2559
|
+
// Trajectory styling is specified in display pixels, while SVG paths use
|
|
2560
|
+
// graph coordinates. Convert at render time so marks stay legible at any scale.
|
|
2561
|
+
graphUnitsForPixels(pixels) {
|
|
2562
|
+
var _a;
|
|
2563
|
+
const extent = this.graph.__data__.extremes.x_max || 1;
|
|
2564
|
+
const width = ((_a = this.graph.svg_element) == null ? void 0 : _a.getBoundingClientRect().width) || 0;
|
|
2565
|
+
return width > 0 ? pixels * (2 * extent / width) : pixels * extent * 6e-3;
|
|
2566
|
+
}
|
|
2567
|
+
create() {
|
|
2568
|
+
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
2569
|
+
group.setAttribute("class", "qe-trajectory-group");
|
|
2570
|
+
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
2571
|
+
path.setAttribute("fill", "none");
|
|
2572
|
+
path.setAttribute("stroke", this.__data__.color.toString());
|
|
2573
|
+
path.setAttribute("stroke-width", this.graphUnitsForPixels(this.__data__.strokeWidth).toString());
|
|
2574
|
+
group.appendChild(path);
|
|
2575
|
+
this._path_el = path;
|
|
2576
|
+
const segmentsGroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
2577
|
+
segmentsGroup.setAttribute("class", "qe-trajectory-segments");
|
|
2578
|
+
group.appendChild(segmentsGroup);
|
|
2579
|
+
this._segments_group = segmentsGroup;
|
|
2580
|
+
const pointsGroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
2581
|
+
pointsGroup.setAttribute("class", "qe-trajectory-points");
|
|
2582
|
+
group.appendChild(pointsGroup);
|
|
2583
|
+
this._points_group = pointsGroup;
|
|
2584
|
+
if (this.__data__.showTips) {
|
|
2585
|
+
const tip = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
2586
|
+
tip.setAttribute("fill", this.__data__.color.toString());
|
|
2587
|
+
tip.setAttribute("r", this.graphUnitsForPixels(4).toString());
|
|
2588
|
+
tip.setAttribute("stroke", "#ffffff");
|
|
2589
|
+
tip.setAttribute("stroke-width", this.graphUnitsForPixels(1.5).toString());
|
|
2590
|
+
group.appendChild(tip);
|
|
2591
|
+
this._tip_el = tip;
|
|
2592
|
+
}
|
|
2593
|
+
this.svg_element = group;
|
|
2594
|
+
return group;
|
|
2595
|
+
}
|
|
2596
|
+
update() {
|
|
2597
|
+
if (!this.svg_element) {
|
|
2598
|
+
this.create();
|
|
2599
|
+
}
|
|
2600
|
+
if (!this.svg_element.parentElement) {
|
|
2601
|
+
this.parent.appendChild(this.svg_element);
|
|
2602
|
+
}
|
|
2603
|
+
const dStr = this.__data__.path;
|
|
2604
|
+
if (this._path_el) {
|
|
2605
|
+
this._path_el.setAttribute("d", dStr);
|
|
2606
|
+
this._path_el.setAttribute("stroke", this.__data__.color.toString());
|
|
2607
|
+
this._path_el.setAttribute("stroke-width", this.graphUnitsForPixels(this.__data__.strokeWidth).toString());
|
|
2608
|
+
this._path_el.style.display = this.__data__.lineStyle === "tapered" ? "none" : "block";
|
|
2609
|
+
}
|
|
2610
|
+
if (this._segments_group) {
|
|
2611
|
+
this._segments_group.innerHTML = "";
|
|
2612
|
+
if (this.__data__.lineStyle === "tapered") {
|
|
2613
|
+
const pathPts = this.__data__.pathPoints;
|
|
2614
|
+
const nSegments = pathPts.length - 1;
|
|
2615
|
+
const [minWidth, maxWidth] = this.__data__.strokeWidthRange;
|
|
2616
|
+
const [minOpacity, maxOpacity] = this.__data__.opacityRange;
|
|
2617
|
+
for (let i = 0; i < nSegments; i++) {
|
|
2618
|
+
const start = pathPts[i];
|
|
2619
|
+
const end = pathPts[i + 1];
|
|
2620
|
+
const progress = nSegments <= 1 ? 1 : i / (nSegments - 1);
|
|
2621
|
+
const width = minWidth + (maxWidth - minWidth) * progress;
|
|
2622
|
+
const opacity = minOpacity + (maxOpacity - minOpacity) * progress;
|
|
2623
|
+
const segment = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
2624
|
+
segment.setAttribute("d", `M${start.x},${start.y} L${end.x},${end.y}`);
|
|
2625
|
+
segment.setAttribute("fill", "none");
|
|
2626
|
+
segment.setAttribute("stroke", this.__data__.color.toString());
|
|
2627
|
+
segment.setAttribute("stroke-linecap", "round");
|
|
2628
|
+
segment.setAttribute("stroke-linejoin", "round");
|
|
2629
|
+
segment.setAttribute("stroke-width", this.graphUnitsForPixels(width).toString());
|
|
2630
|
+
segment.setAttribute("opacity", opacity.toString());
|
|
2631
|
+
this._segments_group.appendChild(segment);
|
|
2632
|
+
}
|
|
2633
|
+
this._segments_group.style.display = "block";
|
|
2634
|
+
} else {
|
|
2635
|
+
this._segments_group.style.display = "none";
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
const pts = this.__data__.activePoints;
|
|
2639
|
+
if (this._points_group) {
|
|
2640
|
+
if (this.__data__.showPoints && pts.length > 0) {
|
|
2641
|
+
this._points_group.innerHTML = "";
|
|
2642
|
+
pts.forEach((pt) => {
|
|
2643
|
+
var _a;
|
|
2644
|
+
const dot = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
2645
|
+
dot.setAttribute("cx", pt.x.toString());
|
|
2646
|
+
dot.setAttribute("cy", pt.y.toString());
|
|
2647
|
+
dot.setAttribute("r", this.graphUnitsForPixels(2.5).toString());
|
|
2648
|
+
dot.setAttribute("fill", this.__data__.color.toString());
|
|
2649
|
+
dot.setAttribute("opacity", "0.7");
|
|
2650
|
+
(_a = this._points_group) == null ? void 0 : _a.appendChild(dot);
|
|
2651
|
+
});
|
|
2652
|
+
this._points_group.style.display = "block";
|
|
2653
|
+
} else {
|
|
2654
|
+
this._points_group.style.display = "none";
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
if (this._tip_el && this.__data__.showTips) {
|
|
2658
|
+
if (pts.length > 0) {
|
|
2659
|
+
const lastPt = pts[pts.length - 1];
|
|
2660
|
+
this._tip_el.setAttribute("cx", lastPt.x.toString());
|
|
2661
|
+
this._tip_el.setAttribute("cy", lastPt.y.toString());
|
|
2662
|
+
this._tip_el.setAttribute("fill", this.__data__.color.toString());
|
|
2663
|
+
this._tip_el.setAttribute("r", this.graphUnitsForPixels(4).toString());
|
|
2664
|
+
this._tip_el.setAttribute("stroke-width", this.graphUnitsForPixels(1.5).toString());
|
|
2665
|
+
this._tip_el.style.display = "block";
|
|
2666
|
+
} else {
|
|
2667
|
+
this._tip_el.style.display = "none";
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2671
|
+
draw() {
|
|
2672
|
+
if (this.svg_element && !this.svg_element.parentElement) {
|
|
2673
|
+
this.parent.appendChild(this.svg_element);
|
|
2674
|
+
}
|
|
2675
|
+
this.update();
|
|
2676
|
+
}
|
|
2677
|
+
};
|
|
2678
|
+
__name(_QETrajectory, "QETrajectory");
|
|
2679
|
+
let QETrajectory = _QETrajectory;
|
|
2267
2680
|
const { ArrayTools } = Tools;
|
|
2268
2681
|
const _QEArray = class _QEArray extends Array {
|
|
2269
2682
|
constructor(...args) {
|
|
@@ -2323,7 +2736,8 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
2323
2736
|
"Edge": QELine,
|
|
2324
2737
|
"TaperedEdge": QELine,
|
|
2325
2738
|
"TaperedWeightedMidpointEdge": QELine,
|
|
2326
|
-
"NodeEdge": QELine
|
|
2739
|
+
"NodeEdge": QELine,
|
|
2740
|
+
"Trajectory": QETrajectory
|
|
2327
2741
|
};
|
|
2328
2742
|
const _QELayer = class _QELayer {
|
|
2329
2743
|
constructor(__data__, parent, graph, __children__ = new QEArray()) {
|
|
@@ -2347,8 +2761,14 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
2347
2761
|
(obj) => !this.__children__.some((child) => childId(child) === obj.ID)
|
|
2348
2762
|
);
|
|
2349
2763
|
adding.forEach((obj) => {
|
|
2350
|
-
const obj_name = obj.constructor.name;
|
|
2351
|
-
|
|
2764
|
+
const obj_name = obj._typeName || obj.constructor.name;
|
|
2765
|
+
let constructor = QEElementConstructors[obj_name];
|
|
2766
|
+
if (!constructor) {
|
|
2767
|
+
if (obj instanceof Trajectory) constructor = QETrajectory;
|
|
2768
|
+
else if (obj instanceof Point) constructor = QEPoint;
|
|
2769
|
+
else constructor = QELine;
|
|
2770
|
+
}
|
|
2771
|
+
const ena_element = new constructor(obj, this.svg_element, this.graph);
|
|
2352
2772
|
this.__children__.push(ena_element);
|
|
2353
2773
|
});
|
|
2354
2774
|
updating.forEach((child) => {
|
|
@@ -2540,10 +2960,22 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
2540
2960
|
__name(_QEEdgesElement, "QEEdgesElement");
|
|
2541
2961
|
_QEEdgesElement.tagName = "qe-edges";
|
|
2542
2962
|
let QEEdgesElement = _QEEdgesElement;
|
|
2963
|
+
const _QETrajectoriesElement = class _QETrajectoriesElement extends HTMLElement {
|
|
2964
|
+
};
|
|
2965
|
+
__name(_QETrajectoriesElement, "QETrajectoriesElement");
|
|
2966
|
+
_QETrajectoriesElement.tagName = "qe-trajectories";
|
|
2967
|
+
let QETrajectoriesElement = _QETrajectoriesElement;
|
|
2968
|
+
const _QEAxesElement = class _QEAxesElement extends HTMLElement {
|
|
2969
|
+
};
|
|
2970
|
+
__name(_QEAxesElement, "QEAxesElement");
|
|
2971
|
+
_QEAxesElement.tagName = "qe-axes";
|
|
2972
|
+
let QEAxesElement = _QEAxesElement;
|
|
2543
2973
|
customElements.define(QENodesElement.tagName, QENodesElement);
|
|
2544
2974
|
customElements.define(QEMeansElement.tagName, QEMeansElement);
|
|
2545
2975
|
customElements.define(QEPointsElement.tagName, QEPointsElement);
|
|
2546
2976
|
customElements.define(QEEdgesElement.tagName, QEEdgesElement);
|
|
2977
|
+
customElements.define(QETrajectoriesElement.tagName, QETrajectoriesElement);
|
|
2978
|
+
customElements.define(QEAxesElement.tagName, QEAxesElement);
|
|
2547
2979
|
const GROUP_PALETTE = [
|
|
2548
2980
|
"#4477AA",
|
|
2549
2981
|
"#EE6677",
|
|
@@ -2617,6 +3049,10 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
2617
3049
|
this._nodeLabels = /* @__PURE__ */ new Map();
|
|
2618
3050
|
this._meanLabels = /* @__PURE__ */ new Map();
|
|
2619
3051
|
this._pointLabels = /* @__PURE__ */ new Map();
|
|
3052
|
+
this._showAxes = false;
|
|
3053
|
+
this._axisXText = null;
|
|
3054
|
+
this._axisYText = null;
|
|
3055
|
+
this._axisLabelEls = /* @__PURE__ */ new Map();
|
|
2620
3056
|
this.graph = this;
|
|
2621
3057
|
this.parent = null;
|
|
2622
3058
|
this.__data__ = new Graph(DefaultGraphOptions);
|
|
@@ -2624,7 +3060,7 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
2624
3060
|
this.svg_element = this.create();
|
|
2625
3061
|
console.log("Created ena-graph svg element: ", this.svg_element);
|
|
2626
3062
|
document.addEventListener("model-updated", (e) => {
|
|
2627
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
3063
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
2628
3064
|
if (this._vizena && e.target !== this._vizena) return;
|
|
2629
3065
|
const model = e.detail;
|
|
2630
3066
|
if (!model) return;
|
|
@@ -2634,6 +3070,9 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
2634
3070
|
const nodeIdCol = model.node_id_col;
|
|
2635
3071
|
const groupColName = model.group_col;
|
|
2636
3072
|
const config = this._readLayerConfig();
|
|
3073
|
+
this._showAxes = config.showAxes;
|
|
3074
|
+
this._axisXText = config.axisXLabel === null ? null : config.axisXLabel || String((dims == null ? void 0 : dims[0]) ?? "x");
|
|
3075
|
+
this._axisYText = config.axisYLabel === null ? null : config.axisYLabel || String((dims == null ? void 0 : dims[1]) ?? "y");
|
|
2637
3076
|
const groupColorMap = {};
|
|
2638
3077
|
if (groupColName && ((_a = model.points) == null ? void 0 : _a.names)) {
|
|
2639
3078
|
const groupCol = model.points.col(groupColName);
|
|
@@ -2667,7 +3106,9 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
2667
3106
|
}, "getExtent");
|
|
2668
3107
|
const maxNodeExtent = ((_c = model.nodes) == null ? void 0 : _c.rows) ? getExtent(model.nodes.rows()) : 0;
|
|
2669
3108
|
const maxPointExtent = ((_d = model.points) == null ? void 0 : _d.rows) ? getExtent(model.points.rows()) : 0;
|
|
2670
|
-
const
|
|
3109
|
+
const maxTrajExtent = ((_e = model.trajectories) == null ? void 0 : _e.rows) ? getExtent(model.trajectories.rows()) : 0;
|
|
3110
|
+
const effectivePointExtent = maxPointExtent > 0 ? maxPointExtent : maxTrajExtent;
|
|
3111
|
+
const pointScale = scalePointsEnabled && effectivePointExtent > 0 && maxNodeExtent > 0 ? maxNodeExtent / effectivePointExtent : 1;
|
|
2671
3112
|
const labelNodes = config.labelNodes;
|
|
2672
3113
|
const labelMeans = config.labelMeans;
|
|
2673
3114
|
const labelPoints = config.labelPoints;
|
|
@@ -2690,7 +3131,7 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
2690
3131
|
}, "rowToPoint");
|
|
2691
3132
|
const groupSums = {};
|
|
2692
3133
|
const showUnitPoints = config.showPoints;
|
|
2693
|
-
if ((
|
|
3134
|
+
if ((_f = model.points) == null ? void 0 : _f.rows) {
|
|
2694
3135
|
const pointsLayer = this.__children__.search("__children__", "points");
|
|
2695
3136
|
if (pointsLayer) {
|
|
2696
3137
|
model.points.rows().forEach((row) => {
|
|
@@ -2699,6 +3140,8 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
2699
3140
|
const colorStr = group ? groupColorMap[group] ?? "#888888" : "#888888";
|
|
2700
3141
|
if (showUnitPoints) {
|
|
2701
3142
|
const pt = rowToPoint(row, unitIdCol, colorStr, pointScale);
|
|
3143
|
+
pt.size = 2.5;
|
|
3144
|
+
pt.meta = { ...pt.meta ?? {}, stroke: "#ffffff" };
|
|
2702
3145
|
this.__data__.points.set(pt.ID, pt);
|
|
2703
3146
|
pointsLayer.__data__.objects.set(pt.ID, pt);
|
|
2704
3147
|
}
|
|
@@ -2716,7 +3159,7 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
2716
3159
|
groupSums[group].n += 1;
|
|
2717
3160
|
}
|
|
2718
3161
|
});
|
|
2719
|
-
const hasGroupsFrame = !!(((
|
|
3162
|
+
const hasGroupsFrame = !!(((_g = model.groups) == null ? void 0 : _g.rows) && model.groups.rows().length > 0);
|
|
2720
3163
|
const activeGroupRows = config.showMeans && hasGroupsFrame ? model.groups.rows().filter((r) => {
|
|
2721
3164
|
const g = r.rowName ?? String(r[0]);
|
|
2722
3165
|
return activeMeanGroups.size === 0 || activeMeanGroups.has(g);
|
|
@@ -2802,7 +3245,7 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
2802
3245
|
this._ciRects.delete(g);
|
|
2803
3246
|
}
|
|
2804
3247
|
});
|
|
2805
|
-
if ((
|
|
3248
|
+
if ((_h = model.confidence) == null ? void 0 : _h.rows) {
|
|
2806
3249
|
model.confidence.rows().forEach((row) => {
|
|
2807
3250
|
const groupIdx = row.names.indexOf(groupColName ?? "group");
|
|
2808
3251
|
const group = groupIdx >= 0 ? String(row[groupIdx]) : row.rowName ?? null;
|
|
@@ -2862,7 +3305,7 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
2862
3305
|
});
|
|
2863
3306
|
}
|
|
2864
3307
|
}
|
|
2865
|
-
if ((
|
|
3308
|
+
if ((_i = model.outlier) == null ? void 0 : _i.rows) {
|
|
2866
3309
|
this._outlierRects.forEach((el, g) => {
|
|
2867
3310
|
if (!groupColorMap[g]) {
|
|
2868
3311
|
el.remove();
|
|
@@ -2951,20 +3394,73 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
2951
3394
|
pointsLayer.__data__.__state__.setVariable("layer_updated", pointsLayer.__data__);
|
|
2952
3395
|
}
|
|
2953
3396
|
}
|
|
3397
|
+
const trajectoriesLayer = this.__children__.search("__children__", "trajectories");
|
|
3398
|
+
const trajectoriesEnabled = config.showTrajectories;
|
|
3399
|
+
if (trajectoriesEnabled && trajectoriesLayer) {
|
|
3400
|
+
trajectoriesLayer.__data__.objects.clear();
|
|
3401
|
+
this.__data__.trajectories.clear();
|
|
3402
|
+
const trajSource = ((_j = model.trajectories) == null ? void 0 : _j.rows) ? model.trajectories : model.points;
|
|
3403
|
+
if (trajSource == null ? void 0 : trajSource.rows) {
|
|
3404
|
+
const unitRows = {};
|
|
3405
|
+
const selectedUnits = config.trajectoryUnits ? config.trajectoryUnits.split(",").map((s) => s.trim()) : null;
|
|
3406
|
+
trajSource.rows().forEach((row) => {
|
|
3407
|
+
const uIdx = row.names.indexOf(unitIdCol) >= 0 ? row.names.indexOf(unitIdCol) : row.names.indexOf("unit") >= 0 ? row.names.indexOf("unit") : row.names.indexOf("ENA_UNIT") >= 0 ? row.names.indexOf("ENA_UNIT") : -1;
|
|
3408
|
+
const unit = uIdx >= 0 ? String(row[uIdx]) : row.rowName || "unit-1";
|
|
3409
|
+
if (selectedUnits && !selectedUnits.includes(unit)) return;
|
|
3410
|
+
const xIdx = row.names.indexOf(dims[0]);
|
|
3411
|
+
const yIdx = dims.length > 1 ? row.names.indexOf(dims[1]) : -1;
|
|
3412
|
+
const tIdx = row.names.indexOf("time") >= 0 ? row.names.indexOf("time") : row.names.indexOf("ActivityNumber") >= 0 ? row.names.indexOf("ActivityNumber") : row.names.indexOf("RowID") >= 0 ? row.names.indexOf("RowID") : -1;
|
|
3413
|
+
const time = tIdx >= 0 ? Number(row[tIdx]) : unitRows[unit] ? unitRows[unit].length + 1 : 1;
|
|
3414
|
+
if (xIdx >= 0 && yIdx >= 0) {
|
|
3415
|
+
if (!unitRows[unit]) unitRows[unit] = [];
|
|
3416
|
+
unitRows[unit].push({
|
|
3417
|
+
x: Number(row[xIdx]) * pointScale,
|
|
3418
|
+
y: -Number(row[yIdx]) * pointScale,
|
|
3419
|
+
time
|
|
3420
|
+
});
|
|
3421
|
+
}
|
|
3422
|
+
});
|
|
3423
|
+
const unitKeys = Object.keys(unitRows);
|
|
3424
|
+
unitKeys.forEach((unitKey, uIdx) => {
|
|
3425
|
+
const rawPts = unitRows[unitKey];
|
|
3426
|
+
if (rawPts.length === 0) return;
|
|
3427
|
+
const colorStr = GROUP_PALETTE[uIdx % GROUP_PALETTE.length];
|
|
3428
|
+
const trajObj = new Trajectory({
|
|
3429
|
+
ID: `traj-${unitKey}`,
|
|
3430
|
+
unitId: unitKey,
|
|
3431
|
+
color: new Color(colorStr),
|
|
3432
|
+
lineStyle: config.trajectoryLineStyle,
|
|
3433
|
+
strokeWidth: config.trajectoryStrokeWidth,
|
|
3434
|
+
strokeWidthRange: config.trajectoryStrokeWidthRange,
|
|
3435
|
+
opacityRange: config.trajectoryOpacityRange,
|
|
3436
|
+
smooth: config.trajectorySmooth,
|
|
3437
|
+
maxDegree: config.trajectoryMaxDegree,
|
|
3438
|
+
degreeSelection: config.trajectoryDegreeSelection,
|
|
3439
|
+
currentTime: config.trajectoryPlayhead ?? Infinity,
|
|
3440
|
+
showPoints: config.trajectoryShowPoints,
|
|
3441
|
+
showTips: config.trajectoryShowTips,
|
|
3442
|
+
points: rawPts.map((p) => ({ x: p.x, y: p.y, time: p.time }))
|
|
3443
|
+
});
|
|
3444
|
+
this.__data__.trajectories.set(unitKey, trajObj);
|
|
3445
|
+
trajectoriesLayer.__data__.objects.set(trajObj.ID, trajObj);
|
|
3446
|
+
});
|
|
3447
|
+
}
|
|
3448
|
+
trajectoriesLayer.__data__.__state__.setVariable("layer_updated", trajectoriesLayer.__data__);
|
|
3449
|
+
}
|
|
2954
3450
|
const networkLayer = this.__children__.search("__children__", "network");
|
|
2955
3451
|
const edgeLayer = networkLayer == null ? void 0 : networkLayer.__children__.search("__children__", "edges");
|
|
2956
3452
|
const nodeLayer = networkLayer == null ? void 0 : networkLayer.__children__.search("__children__", "nodes");
|
|
2957
3453
|
const edgenodeLayer = networkLayer == null ? void 0 : networkLayer.__children__.search("__children__", "edgenodes");
|
|
2958
3454
|
const edgesEnabled = config.showEdges;
|
|
2959
|
-
if (((
|
|
3455
|
+
if (((_k = model.nodes) == null ? void 0 : _k.rows) && nodeLayer) {
|
|
2960
3456
|
model.nodes.rows().forEach((row) => {
|
|
2961
3457
|
const pt = rowToPoint(row, nodeIdCol, "#444444");
|
|
2962
3458
|
pt.size = 2;
|
|
2963
3459
|
nodeLayer.__data__.objects.set(pt.ID, pt);
|
|
2964
3460
|
});
|
|
2965
|
-
if (edgesEnabled && edgeLayer && ((
|
|
3461
|
+
if (edgesEnabled && edgeLayer && ((_l = model.edges) == null ? void 0 : _l.rows)) {
|
|
2966
3462
|
const unitGroupMap = {};
|
|
2967
|
-
if (groupColName && ((
|
|
3463
|
+
if (groupColName && ((_m = model.points) == null ? void 0 : _m.rows)) {
|
|
2968
3464
|
model.points.rows().forEach((row) => {
|
|
2969
3465
|
const qIdx = row.names.indexOf(unitIdCol);
|
|
2970
3466
|
const gIdx = row.names.indexOf(groupColName);
|
|
@@ -3090,7 +3586,6 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
3090
3586
|
totalWeightByPair[key] = (totalWeightByPair[key] ?? 0) + weight;
|
|
3091
3587
|
});
|
|
3092
3588
|
}
|
|
3093
|
-
((_l = this.__data__.extremes) == null ? void 0 : _l.x_max) ?? 1;
|
|
3094
3589
|
drawEdges.forEach(({ col, pair, weight, color }) => {
|
|
3095
3590
|
const fromNode = nodeLayer.__data__.objects.get(pair[0]);
|
|
3096
3591
|
const toNode = nodeLayer.__data__.objects.get(pair[1]);
|
|
@@ -3212,6 +3707,8 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
3212
3707
|
const meansEl = this.querySelector("qe-means");
|
|
3213
3708
|
const pointsEl = this.querySelector("qe-points");
|
|
3214
3709
|
const edgesEl = this.querySelector("qe-edges");
|
|
3710
|
+
const trajectoriesEl = this.querySelector("qe-trajectories");
|
|
3711
|
+
const axesEl = this.querySelector("qe-axes");
|
|
3215
3712
|
const labelsShorthand = this.getAttribute("labels");
|
|
3216
3713
|
return {
|
|
3217
3714
|
// Nodes — always rendered (coordinate anchors); <qe-nodes> controls labels.
|
|
@@ -3239,9 +3736,35 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
3239
3736
|
edgeGroup: (edgesEl == null ? void 0 : edgesEl.getAttribute("group")) ?? null,
|
|
3240
3737
|
edgeUnit: (edgesEl == null ? void 0 : edgesEl.getAttribute("unit")) ?? null,
|
|
3241
3738
|
edgeCompare: (edgesEl == null ? void 0 : edgesEl.getAttribute("compare")) ?? null,
|
|
3242
|
-
edgeAlso: (edgesEl == null ? void 0 : edgesEl.getAttribute("also")) ?? null
|
|
3739
|
+
edgeAlso: (edgesEl == null ? void 0 : edgesEl.getAttribute("also")) ?? null,
|
|
3740
|
+
// Trajectories — presence of <qe-trajectories> = draw cognitive trajectories
|
|
3741
|
+
showTrajectories: !!trajectoriesEl,
|
|
3742
|
+
trajectoryUnits: (trajectoriesEl == null ? void 0 : trajectoriesEl.getAttribute("units")) ?? null,
|
|
3743
|
+
trajectorySmooth: (trajectoriesEl == null ? void 0 : trajectoriesEl.getAttribute("smooth")) ?? "none",
|
|
3744
|
+
trajectoryMaxDegree: (trajectoriesEl == null ? void 0 : trajectoriesEl.getAttribute("max-degree")) ? parseInt(trajectoriesEl.getAttribute("max-degree"), 10) : 3,
|
|
3745
|
+
trajectoryDegreeSelection: (trajectoriesEl == null ? void 0 : trajectoriesEl.getAttribute("degree-selection")) ?? "loocv_2d",
|
|
3746
|
+
trajectoryShowPoints: (trajectoriesEl == null ? void 0 : trajectoriesEl.getAttribute("points")) !== "false",
|
|
3747
|
+
trajectoryShowTips: (trajectoriesEl == null ? void 0 : trajectoriesEl.getAttribute("tips")) !== "false",
|
|
3748
|
+
trajectoryPlayhead: (trajectoriesEl == null ? void 0 : trajectoriesEl.getAttribute("playhead")) ? parseFloat(trajectoriesEl.getAttribute("playhead")) : null,
|
|
3749
|
+
trajectoryLineStyle: (trajectoriesEl == null ? void 0 : trajectoriesEl.getAttribute("line-style")) ?? "uniform",
|
|
3750
|
+
trajectoryStrokeWidth: (trajectoriesEl == null ? void 0 : trajectoriesEl.getAttribute("stroke-width")) ? parseFloat(trajectoriesEl.getAttribute("stroke-width")) : 2,
|
|
3751
|
+
trajectoryStrokeWidthRange: this._parseNumberPair(trajectoriesEl == null ? void 0 : trajectoriesEl.getAttribute("stroke-width-range"), [1, 6]),
|
|
3752
|
+
trajectoryOpacityRange: this._parseNumberPair(trajectoriesEl == null ? void 0 : trajectoriesEl.getAttribute("opacity-range"), [0.3, 1]),
|
|
3753
|
+
// Axes — presence of <qe-axes> = draw axis lines. The x / y attributes add
|
|
3754
|
+
// a per-axis label: a value is used verbatim, an empty attribute falls back
|
|
3755
|
+
// to the dimension name (resolved later, where dims are known). null = no
|
|
3756
|
+
// label for that axis.
|
|
3757
|
+
showAxes: !!axesEl,
|
|
3758
|
+
axisXLabel: (axesEl == null ? void 0 : axesEl.hasAttribute("x")) ? axesEl.getAttribute("x") ?? "" : null,
|
|
3759
|
+
axisYLabel: (axesEl == null ? void 0 : axesEl.hasAttribute("y")) ? axesEl.getAttribute("y") ?? "" : null
|
|
3243
3760
|
};
|
|
3244
3761
|
}
|
|
3762
|
+
_parseNumberPair(value, fallback) {
|
|
3763
|
+
if (!value) return fallback;
|
|
3764
|
+
const parts = value.split(",").map((part) => Number(part.trim()));
|
|
3765
|
+
if (parts.length < 2 || !parts.every(Number.isFinite)) return fallback;
|
|
3766
|
+
return [parts[0], parts[1]];
|
|
3767
|
+
}
|
|
3245
3768
|
attributeChangedCallback(_name2, oldValue, newValue) {
|
|
3246
3769
|
if (oldValue === newValue) return;
|
|
3247
3770
|
this._scheduleRerender();
|
|
@@ -3250,6 +3773,7 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
3250
3773
|
var _a;
|
|
3251
3774
|
const w = this.getAttribute("width");
|
|
3252
3775
|
const h = this.getAttribute("height");
|
|
3776
|
+
this.style.display = "block";
|
|
3253
3777
|
if (w) this.style.width = isNaN(Number(w)) ? w : `${w}px`;
|
|
3254
3778
|
if (h) this.style.height = isNaN(Number(h)) ? h : `${h}px`;
|
|
3255
3779
|
this.appendChild(this.svg_element);
|
|
@@ -3270,7 +3794,19 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
3270
3794
|
"labels",
|
|
3271
3795
|
"label-nodes",
|
|
3272
3796
|
"label-means",
|
|
3273
|
-
"label-points"
|
|
3797
|
+
"label-points",
|
|
3798
|
+
"units",
|
|
3799
|
+
"smooth",
|
|
3800
|
+
"degree",
|
|
3801
|
+
"points",
|
|
3802
|
+
"tips",
|
|
3803
|
+
"playhead",
|
|
3804
|
+
"line-style",
|
|
3805
|
+
"stroke-width",
|
|
3806
|
+
"stroke-width-range",
|
|
3807
|
+
"opacity-range",
|
|
3808
|
+
"x",
|
|
3809
|
+
"y"
|
|
3274
3810
|
]
|
|
3275
3811
|
});
|
|
3276
3812
|
const forId = this.getAttribute("for");
|
|
@@ -3297,6 +3833,10 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
3297
3833
|
create() {
|
|
3298
3834
|
const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
3299
3835
|
svg.classList.add("graph");
|
|
3836
|
+
svg.style.display = "block";
|
|
3837
|
+
svg.style.width = "100%";
|
|
3838
|
+
svg.style.height = "100%";
|
|
3839
|
+
svg.setAttribute("preserveAspectRatio", "xMidYMid meet");
|
|
3300
3840
|
svg.setAttribute("viewBox", this.__data__.extremes.as_viewBox());
|
|
3301
3841
|
svg.setAttribute("transform", this.__data__.transform.toString());
|
|
3302
3842
|
svg.setAttribute("role", "figure");
|
|
@@ -3423,6 +3963,56 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
3423
3963
|
this.update_layers();
|
|
3424
3964
|
this.update_labels();
|
|
3425
3965
|
this.update_extremes();
|
|
3966
|
+
this.update_axes();
|
|
3967
|
+
}
|
|
3968
|
+
/**
|
|
3969
|
+
* Axes (<qe-axes>): toggle the axis-line cross by presence, and draw/position
|
|
3970
|
+
* per-axis labels at the positive ends using the current extent. Labels are
|
|
3971
|
+
* cached SVG <text> nodes in the labels group; null text removes them.
|
|
3972
|
+
*/
|
|
3973
|
+
update_axes() {
|
|
3974
|
+
const axesLayer = this.__children__.search("__children__", "axes");
|
|
3975
|
+
if (axesLayer == null ? void 0 : axesLayer.svg_element) {
|
|
3976
|
+
axesLayer.svg_element.style.display = this._showAxes ? "" : "none";
|
|
3977
|
+
}
|
|
3978
|
+
const parent = this._labelsGroup;
|
|
3979
|
+
const extent = this.__data__.extremes.x_max;
|
|
3980
|
+
if (!parent || extent <= 0) return;
|
|
3981
|
+
const fontSize = extent * 0.06;
|
|
3982
|
+
const gap = fontSize * 0.6;
|
|
3983
|
+
const place = /* @__PURE__ */ __name((axis, text) => {
|
|
3984
|
+
let el = this._axisLabelEls.get(axis);
|
|
3985
|
+
if (!this._showAxes || text == null) {
|
|
3986
|
+
if (el) {
|
|
3987
|
+
el.remove();
|
|
3988
|
+
this._axisLabelEls.delete(axis);
|
|
3989
|
+
}
|
|
3990
|
+
return;
|
|
3991
|
+
}
|
|
3992
|
+
if (!el) {
|
|
3993
|
+
el = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
3994
|
+
el.setAttribute("data-axis-label", axis);
|
|
3995
|
+
el.setAttribute("pointer-events", "none");
|
|
3996
|
+
parent.appendChild(el);
|
|
3997
|
+
this._axisLabelEls.set(axis, el);
|
|
3998
|
+
}
|
|
3999
|
+
el.textContent = text;
|
|
4000
|
+
el.setAttribute("font-size", fontSize.toString());
|
|
4001
|
+
el.setAttribute("font-family", "system-ui, sans-serif");
|
|
4002
|
+
el.setAttribute("font-weight", "600");
|
|
4003
|
+
el.setAttribute("fill", "#556");
|
|
4004
|
+
if (axis === "x") {
|
|
4005
|
+
el.setAttribute("x", (extent + gap).toString());
|
|
4006
|
+
el.setAttribute("y", (fontSize * 0.35).toString());
|
|
4007
|
+
el.setAttribute("text-anchor", "start");
|
|
4008
|
+
} else {
|
|
4009
|
+
el.setAttribute("x", "0");
|
|
4010
|
+
el.setAttribute("y", (-extent - gap).toString());
|
|
4011
|
+
el.setAttribute("text-anchor", "middle");
|
|
4012
|
+
}
|
|
4013
|
+
}, "place");
|
|
4014
|
+
place("x", this._axisXText);
|
|
4015
|
+
place("y", this._axisYText);
|
|
3426
4016
|
}
|
|
3427
4017
|
update_labels() {
|
|
3428
4018
|
this._nodeLabels.forEach((lbl) => lbl.update());
|
|
@@ -3495,6 +4085,7 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
3495
4085
|
exports2.DataFrameRow = DataFrameRow;
|
|
3496
4086
|
exports2.Edge = Edge;
|
|
3497
4087
|
exports2.Graph = Graph;
|
|
4088
|
+
exports2.QEAxesElement = QEAxesElement;
|
|
3498
4089
|
exports2.QEEdgesElement = QEEdgesElement;
|
|
3499
4090
|
exports2.QEGraphElement = QEGraphElement;
|
|
3500
4091
|
exports2.QELayer = QELayer;
|
|
@@ -3503,8 +4094,16 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
3503
4094
|
exports2.QENodesElement = QENodesElement;
|
|
3504
4095
|
exports2.QEPoint = QEPoint;
|
|
3505
4096
|
exports2.QEPointsElement = QEPointsElement;
|
|
4097
|
+
exports2.QETrajectoriesElement = QETrajectoriesElement;
|
|
4098
|
+
exports2.QETrajectory = QETrajectory;
|
|
3506
4099
|
exports2.QEVisualElement = QEVisualElement;
|
|
4100
|
+
exports2.TRAJECTORY_ZERO_EPS = TRAJECTORY_ZERO_EPS;
|
|
4101
|
+
exports2.Trajectory = Trajectory;
|
|
3507
4102
|
exports2.WeightedColor = WeightedColor;
|
|
4103
|
+
exports2.fitTrajectory = fitTrajectory;
|
|
4104
|
+
exports2.nearOriginFilter = nearOriginFilter;
|
|
4105
|
+
exports2.sampleCurve = sampleCurve;
|
|
4106
|
+
exports2.selectDegree = selectDegree;
|
|
3508
4107
|
exports2.vizENA = vizENA;
|
|
3509
4108
|
Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
|
|
3510
4109
|
}));
|