// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ // ┃ ██████ ██████ ██████ █ █ █ █ █ █▄ ▀███ █ ┃ // ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█ ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄ ▀█ █ ▀▀▀▀▀ ┃ // ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄ █ ▄▄▄▄▄ ┃ // ┃ █ ██████ █ ▀█▄ █ ██████ █ ███▌▐███ ███████▄ █ ┃ // ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫ // ┃ Copyright (c) 2017, the Perspective Authors. ┃ // ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃ // ┃ This file is part of the Perspective library, distributed under the terms ┃ // ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃ // ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ import { AbstractChart } from "../chart-base"; import type { ColumnDataMap } from "../../data/view-reader"; import { NodeStore, NULL_NODE } from "./node-store"; import { LazyTooltip } from "../../interaction/lazy-tooltip"; /** * Sentinel fallback for the Size slot when the user hasn't picked one: * use the first non-metadata column in the incoming view. Tree charts * still need *some* numeric-ish column to size geometry. */ export function firstNonMetadataColumn(columns: ColumnDataMap): string { for (const k of columns.keys()) { if (!k.startsWith("__")) { return k; } } return ""; } /** * Shared state for hierarchical charts (treemap, sunburst). Holds the * tree store + streaming-insert scaffolding + per-row tooltip data * buffers. Concrete chart classes extend this and add their own * layout / render / interact state. * * Fields are `public` so the `tree-data.ts` helpers and per-chart * layout modules can read/write them without friction. */ export abstract class TreeChartBase extends AbstractChart { // Shared column-slot resolution _sizeName = ""; _colorName = ""; /** * Color-slot semantics. * - `"empty"`: no Color slot → single palette[0], legend suppressed. * - `"numeric"`: Color column is float / integer / date / datetime → * continuous gradient via `colorValueToT`. * - `"series"`: Color column is any other type → discrete series * palette keyed by the composite of group_by level values. */ _colorMode: "empty" | "numeric" | "series" = "empty"; // Tree storage (SOA + linked-list children) _nodeStore: NodeStore = new NodeStore(); _rootId: number = NULL_NODE; _currentRootId: number = NULL_NODE; _breadcrumbIds: number[] = []; /** * Per-parent `Map` for O(1) find-or-create * during streaming tree insertion. Rebuilt on each dataset reset. */ _childLookup: Map> = new Map(); // Streaming-insert row counter // Source-view row offset tracked across chunks so `leafRowIdx` on // each leaf points back to the correct view row for lazy tooltip // fetches via `AbstractChart._lazyRows`. _rowCount = 0; // Color extents / categorical key table _colorMin = Infinity; _colorMax = -Infinity; _uniqueColorLabels: Map = new Map(); // Visible-node cache (populated per frame by layout/collect) ────── _visibleNodeIds: Int32Array | null = null; _visibleNodeCount = 0; /** * Lazy-tooltip cache. `lines` is `null` until the async row fetch * resolves; the controller's serial dance drops stale results so * rapid mouse motion doesn't paint out-of-date text. The render * path consults `hoveredTarget` to verify cached lines belong to * the currently hovered node before painting. */ _lazyTooltip = new LazyTooltip(); }