@tanstack/charts
Version:
A chart grammar for TypeScript and JavaScript. Marks consume your data directly, channels describe visual encodings, and the engine compiles them into a renderer-neutral keyed scene. TanStack's compact scales cover common numeric and categorical mappings.
93 lines (92 loc) • 3.03 kB
JavaScript
import { Delaunay } from "d3-delaunay";
import { compareChartKey } from "./scales.js";
function canonicalDelaunayPoints(points) {
const ordered = [...points].sort(comparePointIdentity);
const seen = /* @__PURE__ */ new Map();
return ordered.filter((point) => {
const yValues = seen.get(point.x);
if (yValues?.has(point.y)) return false;
if (yValues) yValues.add(point.y);
else seen.set(point.x, /* @__PURE__ */ new Set([point.y]));
return true;
});
}
function createDelaunay(points) {
return Delaunay.from(
points,
(point) => point.x,
(point) => point.y
);
}
function delaunayNeighborIndexes(delaunay, pointCount, includeDegenerateTriangles = false) {
const adjacency = Array.from({ length: pointCount }, () => /* @__PURE__ */ new Set());
const addEdge = (source, target) => {
if (source < 0 || target < 0 || source >= pointCount || target >= pointCount || source === target) {
return;
}
adjacency[source].add(target);
adjacency[target].add(source);
};
const { triangles, hull } = delaunay;
const collinear = delaunay.collinear;
if (collinear) {
for (let index = 1; index < collinear.length; index += 1) {
addEdge(collinear[index - 1], collinear[index]);
}
}
if (!collinear || includeDegenerateTriangles) {
for (let index = 0; index < triangles.length; index += 3) {
const first = triangles[index];
const second = triangles[index + 1];
const third = triangles[index + 2];
addEdge(first, second);
addEdge(second, third);
addEdge(third, first);
}
for (let index = 0; index < hull.length; index += 1) {
addEdge(hull[index], hull[(index + 1) % hull.length]);
}
}
return adjacency.map((neighbors) => [...neighbors]);
}
function angularDelaunayNeighborIndexes(delaunay, pointCount, includeDegenerateTriangles = false) {
const { points } = delaunay;
return delaunayNeighborIndexes(
delaunay,
pointCount,
includeDegenerateTriangles
).map(
(neighbors, source) => [...neighbors].sort(
(left, right) => Math.atan2(
points[left * 2 + 1] - points[source * 2 + 1],
points[left * 2] - points[source * 2]
) - Math.atan2(
points[right * 2 + 1] - points[source * 2 + 1],
points[right * 2] - points[source * 2]
)
)
);
}
function delaunayNeighborPairs(points) {
if (points.length < 2) return [];
const delaunay = createDelaunay(points);
const pairs = [];
const neighbors = delaunayNeighborIndexes(delaunay, points.length);
for (let source = 0; source < points.length; source += 1) {
for (const target of neighbors[source]) {
if (target <= source) continue;
pairs.push([source, target]);
}
}
return pairs;
}
function comparePointIdentity(left, right) {
return compareChartKey(left.key, right.key) || left.sourceIndex - right.sourceIndex;
}
export {
angularDelaunayNeighborIndexes,
canonicalDelaunayPoints,
createDelaunay,
delaunayNeighborIndexes,
delaunayNeighborPairs
};