UNPKG

highcharts

Version:
1,415 lines (1,362 loc) 52.5 kB
/** * @license Highmaps JS v13.0.1 (2026-08-17) * @module highcharts/modules/contour * @requires highcharts * @requires highcharts/modules/coloraxis * * (c) 2009-2025 Highsoft AS * * License: www.highcharts.com/license */ import * as __WEBPACK_EXTERNAL_MODULE__highcharts_src_js_8202131d__ from "../highcharts.src.js"; import "./coloraxis.src.js"; /******/ // The require scope /******/ const __webpack_require__ = {}; /******/ /************************************************************************/ /******/ /* webpack/runtime/compat get default export */ /******/ (() => { /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = (module) => { /******/ const getter = module && module.__esModule ? /******/ () => (module['default']) : /******/ () => (module); /******/ __webpack_require__.d(getter, { a: getter }); /******/ return getter; /******/ }; /******/ })(); /******/ /******/ /* webpack/runtime/define property getters */ /******/ (() => { /******/ // define getter/value functions for harmony exports /******/ __webpack_require__.d = (exports, definition) => { /******/ if(Array.isArray(definition)) { /******/ var i = 0; /******/ while(i < definition.length) { /******/ var key = definition[i++]; /******/ var binding = definition[i++]; /******/ if(!__webpack_require__.o(exports, key)) { /******/ if(binding === 0) { /******/ Object.defineProperty(exports, key, { enumerable: true, value: definition[i++] }); /******/ } else { /******/ Object.defineProperty(exports, key, { enumerable: true, get: binding }); /******/ } /******/ } else if(binding === 0) { i++; } /******/ } /******/ } else { /******/ for(var key in definition) { /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); /******/ } /******/ } /******/ } /******/ }; /******/ })(); /******/ /******/ /* webpack/runtime/hasOwnProperty shorthand */ /******/ (() => { /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) /******/ })(); /******/ /************************************************************************/ ;// external ["../highcharts.src.js","default"] const external_highcharts_src_js_default_namespaceObject = __WEBPACK_EXTERNAL_MODULE__highcharts_src_js_8202131d__["default"]; var external_highcharts_src_js_default_default = /*#__PURE__*/__webpack_require__.n(external_highcharts_src_js_default_namespaceObject); ;// external "./coloraxis.src.js" ;// external ["../highcharts.src.js","default","Color"] const external_highcharts_src_js_default_Color_namespaceObject = __WEBPACK_EXTERNAL_MODULE__highcharts_src_js_8202131d__["default"].Color; var external_highcharts_src_js_default_Color_default = /*#__PURE__*/__webpack_require__.n(external_highcharts_src_js_default_Color_namespaceObject); ;// external ["../highcharts.src.js","default","SeriesRegistry"] const external_highcharts_src_js_default_SeriesRegistry_namespaceObject = __WEBPACK_EXTERNAL_MODULE__highcharts_src_js_8202131d__["default"].SeriesRegistry; var external_highcharts_src_js_default_SeriesRegistry_default = /*#__PURE__*/__webpack_require__.n(external_highcharts_src_js_default_SeriesRegistry_namespaceObject); ;// ./code/es-modules/Series/Contour/ContourPoint.js /* * * * (c) 2010-2026 Highsoft AS * Author: Torstein Hønsi * * Integration of this software requires a license. * - For commercial use, see www.highcharts.com/license * - For non-commercial, see www.highcharts.com/license-eula * * * */ /* * * * Imports * * */ const { scatter: { prototype: { pointClass: ScatterPoint } } } = (external_highcharts_src_js_default_SeriesRegistry_default()).seriesTypes; /* * * * Class * * */ /** @internal */ class ContourPoint extends ScatterPoint { } /* * * * Default Export * * */ /** @internal */ /* harmony default export */ const Contour_ContourPoint = (ContourPoint); ;// ./code/es-modules/Series/Contour/ContourShader.js /* * * * (c) 2010-2026 Highsoft AS * Author: Torstein Hønsi * * Integration of this software requires a license. * - For commercial use, see www.highcharts.com/license * - For non-commercial, see www.highcharts.com/license-eula * * * */ /* * * * Shader Code * * */ /** @internal */ const ContourShader = ` struct VertexInput { @location(0) pos: vec3f } struct VertexOutput { @builtin(position) pos: vec4f, @location(0) originalPos: vec3f, @location(1) valExtremes: vec2f, } @group(0) @binding(0) var<uniform> uExtremes: vec4f; @group(0) @binding(1) var<uniform> uValueExtremes: vec2f; @group(0) @binding(9) var<uniform> uIsInverted: u32; @vertex fn vertexMain(input: VertexInput) -> VertexOutput { var output: VertexOutput; let pos = input.pos; let xMin = uExtremes[0]; let xMax = uExtremes[1]; let yMin = uExtremes[2]; let yMax = uExtremes[3]; var posX: f32; var posY: f32; if (uIsInverted > 0u) { posX = (1.0 - (pos.y - yMin) / (yMax - yMin)) * 2.0 - 1.0; posY = (1.0 - (pos.x - xMin) / (xMax - xMin)) * 2.0 - 1.0; } else { posX = (pos.x - xMin) / (xMax - xMin) * 2.0 - 1.0; posY = (pos.y - yMin) / (yMax - yMin) * 2.0 - 1.0; } output.valExtremes = uValueExtremes; output.originalPos = pos.xyz; output.pos = vec4f(posX, posY, 0, 1); return output; } // ------------------------------------------------ struct FragmentInput { @location(0) originalPos: vec3f, @location(1) valExtremes: vec2f } @group(0) @binding(2) var<storage> uColorStops: array<vec4<f32>>; @group(0) @binding(3) var<uniform> uColorStopsCount: u32; @group(0) @binding(4) var<uniform> uContourInterval: f32; @group(0) @binding(5) var<uniform> uContourOffset: f32; @group(0) @binding(6) var<uniform> uSmoothColoring: u32; @group(0) @binding(7) var<uniform> uContourLineWidth: f32; @group(0) @binding(8) var<uniform> uContourLineColor: vec3f; fn getColor(value: f32) -> vec3<f32> { let stopCount = uColorStopsCount; if (stopCount == 0u) { return vec3<f32>(1.0, 1.0, 1.0); } for (var i: u32 = 0u; i < stopCount - 1u; i = i + 1u) { if (value < uColorStops[i + 1u].x) { let t = (value - uColorStops[i].x) / (uColorStops[i + 1u].x - uColorStops[i].x); return mix(uColorStops[i].yzw, uColorStops[i + 1u].yzw, t); } } return uColorStops[stopCount - 1u].yzw; } @fragment fn fragmentMain(input: FragmentInput) -> @location(0) vec4f { let val = input.originalPos.z; // Contour lines let lineWidth: f32 = uContourLineWidth; let val_dx: f32 = dpdx(val); let val_dy: f32 = dpdy(val); let gradient: f32 = length(vec2f(val_dx, val_dy)); let epsilon: f32 = max(uContourInterval * 1.0e-6, 1.0e-12); let adjustedLineWidth: f32 = lineWidth * gradient + epsilon; let adjustedVal: f32 = val - uContourOffset; let valDiv: f32 = adjustedVal / uContourInterval; let valMod: f32 = adjustedVal - uContourInterval * floor(valDiv); let lineMask: f32 = smoothstep(0.0, adjustedLineWidth, valMod) * ( 1.0 - smoothstep( uContourInterval - adjustedLineWidth, uContourInterval, valMod ) ); // Background color let minHeight: f32 = input.valExtremes.x; let maxHeight: f32 = input.valExtremes.y; var bgColor: vec3f; if (uSmoothColoring > 0u) { bgColor = getColor((val - minHeight) / (maxHeight - minHeight)); } else { let adjustedVal: f32 = val - uContourOffset; let averageValInBand: f32 = floor( adjustedVal / uContourInterval ) * uContourInterval + uContourOffset + uContourInterval / 2.0; bgColor = getColor( (averageValInBand - minHeight) / (maxHeight - minHeight) ); } // Mix var pixelColor = bgColor; if (lineWidth > 0.0) { pixelColor = mix(uContourLineColor, pixelColor, lineMask); } return vec4(pixelColor, 1.0); } `; /* * * * Default Export * * */ /** @internal */ /* harmony default export */ const Contour_ContourShader = (ContourShader); ;// ./code/es-modules/Series/Contour/ContourSeriesDefaults.js /* * * * (c) 2010-2026 Highsoft AS * Author: Torstein Hønsi * * Integration of this software requires a license. * - For commercial use, see www.highcharts.com/license * - For non-commercial, see www.highcharts.com/license-eula * * * */ /* * * * Definitions * * */ /** * A contour plot is a graphical representation of three-dimensional data * * @productdesc {highcharts} * Requires `modules/contour`. * * @sample highcharts/demo/contour-mountain/ * * @extends plotOptions.scatter * @excluding animationLimit, cluster, connectEnds, connectNulls, * cropThreshold, dashStyle, dragDrop, getExtremesFromAll, * jitter, legendSymbolColor, linecap, pointInterval, * pointIntervalUnit, pointRange, pointStart, shadow, * softThreshold, stacking, step, threshold * * @product highcharts highmaps * @requires modules/coloraxis * @requires modules/contour * @optionparent plotOptions.contour */ const ContourSeriesDefaults = { /** * This must be set to `'value'` to make the colorAxis track with the contour * plot. */ colorKey: 'value', clip: false, /** * Whether to use gradually transitioning color gradients between contour * levels. When disabled, each contour level is filled with a single flat * color. * * @type {boolean} * @default false * @apioption plotOptions.contour.smoothColoring */ /** * The color of the contour lines. * * @type {Highcharts.ColorType} * @default #000000 * @apioption plotOptions.contour.lineColor */ /** * This setting controls the visibility and size of contour lines. For now, * only '1' and '0' are valid options, effectively controlling the * visibility of the lines. * * @type {number} * @default 1 * @apioption plotOptions.contour.lineWidth */ /** * The interval between contour lines. Determines the spacing of value * levels where lines are drawn on the plot. By default, the interval is * calculated using the value range. * * @type {number} * @apioption plotOptions.contour.contourInterval */ /** * The offset for contour line positioning. Shifts the contour levels so * lines and bands are drawn at `contourOffset + n * contourInterval` * instead of `n * contourInterval`. * * Example: with `contourInterval: 10` and `contourOffset: 5`, levels are * at 5, 15, 25, etc. Use this to align levels with a reference value * without changing the data. Non-positive values are treated as 0. * * @type {number} * @default 0 * @apioption plotOptions.contour.contourOffset */ /** * @excluding radius, enabledThreshold, fillColor, lineColor */ marker: { /** * A predefined shape or symbol for the marker. When undefined, the * symbol is pulled from options.symbols. Other possible values are * `'circle'`, `'square'`,`'diamond'`, `'triangle'`, * `'triangle-down'`, `'rect'`, `'ellipse'`, and `'cross'`. * * Additionally, the URL to a graphic can be given on this form: * `'url(graphic.png)'`. Note that for the image to be applied to * exported charts, its URL needs to be accessible by the export * server. * * Custom callbacks for symbol path generation can also be added to * `Highcharts.SVGRenderer.prototype.symbols`. The callback is then * used by its method name, as shown in the demo. * * @sample {highcharts} highcharts/plotoptions/series-marker-symbol/ * Predefined, graphic and custom markers * @sample {highstock} highcharts/plotoptions/series-marker-symbol/ * Predefined, graphic and custom markers */ symbol: 'cross', states: { /** * @excluding radius, radiusPlus */ hover: { /** * Color of the marker outline. Defaults to `'black'`. * * @type {string} * * @default black * * @apioption plotOptions.contour.marker.states.hover.lineColor */ lineColor: 'black' } } }, states: { hover: { /** @ignore-option */ halo: void 0 } }, zIndex: 0 }; /** * A contour plot is a graphical representation of three-dimensional data * in two dimensions using contour lines or color-coded regions. * * @productdesc {highcharts} * Requires `modules/contour`. * * @sample highcharts/demo/contour-mountain/ * Simple contour * * @extends series,plotOptions.contour * @excluding cropThreshold, dataParser, dataURL, dragDrop ,pointRange, * stack, allowPointSelect, boostBlending, boostThreshold, color, * colorIndex, connectEnds, connectNulls, crisp, dashStyle, * inactiveOtherPoints, jitter, linecap, negativeColor, * pointInterval, pointStart, pointIntervalUnit, lineWidth, * onPoint, pointPlacement, shadow, stacking, step, threshold, * zoneAxis, zones, onPoint, grouping, groupPadding, * groupZPadding * * * @product highcharts highmaps * @requires modules/coloraxis * @requires modules/contour * @apioption series.contour */ /** * An array of data points for the series. For the `contour` series * type, points can be given in the following ways: * * 1. An array of arrays with 3 or 2 values. In this case, the values * correspond to `x,y,value`. If the first value is a string, it is * applied as the name of the point, and the `x` value is inferred. * Unlike the heatmap, the contour series data points, do not have to appear * in any specific order. * * ```js * data: [ * [0, 9, 7], * [1, 10, 4], * [2, 6, 3] * ] * ``` * * 2. An array of objects with named values. The following snippet shows only a * few settings, see the complete options set below. If the total number of data * points exceeds the series' [turboThreshold](#series.contour.turboThreshold), * this option is not available. * * ```js * data: [{ * x: 1, * y: 3, * value: 10, * name: "Point2" * }, { * x: 1, * y: 7, * value: 10, * name: "Point1" * }] * ``` * * @sample {highcharts} highcharts/chart/reflow-true/ * Numerical values * @sample {highcharts} highcharts/series/data-array-of-arrays/ * Arrays of numeric x and y * @sample {highcharts} highcharts/series/data-array-of-arrays-datetime/ * Arrays of datetime x and y * @sample {highcharts} highcharts/series/data-array-of-name-value/ * Arrays of point.name and y * @sample {highcharts} highcharts/series/data-array-of-objects/ * Config objects * * @basic * @type {Array<Array<number>|*>} * @extends series.line.data * @product highcharts highmaps * @apioption series.contour.data */ /** * The value of the point, resulting in a color controlled by options * as set in the [colorAxis](#colorAxis) configuration. * * @type {number} * @product highcharts highmaps * @apioption series.contour.data.value */ /** * The x value of the point. For datetime axes, * the X value is the timestamp in milliseconds since 1970. * * @type {number} * @product highcharts highmaps * @apioption series.contour.data.x */ /** * The y value of the point. * * @type {number} * @product highcharts highmaps * @apioption series.contour.data.y */ ''; // Keeps doclets above separate /* harmony default export */ const Contour_ContourSeriesDefaults = (ContourSeriesDefaults); ;// ./code/es-modules/Series/CrossSymbol.js /* * * * (c) 2010-2026 Highsoft AS * Authors: Kamil Musiałowski, Markus Barstad * * Shared cross marker symbol registration used by series modules. * This keeps `cross` out of Core SVG symbols while allowing modules * like PointAndFigure and Contour to compose it when needed. * * Integration of this software requires a license. * - For commercial use, see www.highcharts.com/license * - For non-commercial, see www.highcharts.com/license-eula * * * */ const { composed } = (external_highcharts_src_js_default_default()); /* * * * Composition * * */ var CrossSymbol; (function (CrossSymbol) { /* * * * Functions * * */ /** * Register the shared `cross` symbol on a renderer class. * * @internal */ function compose(SVGRendererClass) { if ((0,external_highcharts_src_js_default_namespaceObject.pushUnique)(composed, 'Series.CrossSymbol')) { SVGRendererClass.prototype.symbols.cross = cross; } } CrossSymbol.compose = compose; /** * Cross marker path. * @internal */ function cross(x, y, w, h) { return [ ['M', x, y], ['L', x + w, y + h], ['M', x + w, y], ['L', x, y + h], ['Z'] ]; } })(CrossSymbol || (CrossSymbol = {})); /* * * * Default Export * * */ /** @internal */ /* harmony default export */ const Series_CrossSymbol = (CrossSymbol); ;// ./code/es-modules/Core/Delaunay.js /* * * * (c) 2009-2026 Highsoft AS * * Integration of this software requires a license. * - For commercial use, see www.highcharts.com/license * - For non-commercial, see www.highcharts.com/license-eula * * * Authors: * - Dawid Draguła * * */ /* * * * Class * * */ /** * Delaunay triangulation of a 2D point set. * * @internal */ class Delaunay { /* * * * Constructor * * */ /** * Create a new Delaunay triangulation. * * @param {Float32Array|Float64Array} points * A 1D array of points in the format [x0, y0, x1, y1, ...]. */ constructor(points) { this.points = points; const n = points.length >>> 1; // Floating-point error multiplier used by geometric predicates. this.epsilon = 4 * Number.EPSILON; let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity; for (let i = 0; i < n; i++) { const px = points[i << 1], py = points[(i << 1) + 1]; if (px < minX) { minX = px; } if (px > maxX) { maxX = px; } if (py < minY) { minY = py; } if (py > maxY) { maxY = py; } } const rangeX = maxX - minX || 1, rangeY = maxY - minY || 1; this.minX = minX; this.minY = minY; this.invScaleX = 1 / rangeX; this.invScaleY = 1 / rangeY; const ids = new Uint32Array(n), x = (i) => (points[i << 1] - minX) * this.invScaleX, y = (i) => (points[(i << 1) + 1] - minY) * this.invScaleY; for (let i = 0; i < n; i++) { ids[i] = i; } ids.sort((a, b) => (x(a) - x(b)) || (y(a) - y(b))); let m = n ? 1 : 0, pa, pb; for (let i = 1; i < n; ++i) { pa = ids[m - 1], pb = ids[i]; if (x(pa) !== x(pb) || y(pa) !== y(pb)) { ids[m++] = pb; } } this.ids = ids.subarray(0, m); this.triangles = this.triangulate(); } /* * * * Methods * * */ /** * Triangulate the points. * * @return {Uint32Array} * A 1D array of triangle vertex indices. */ triangulate() { const count = this.ids.length; if (count < 3) { return new Uint32Array(0); } const points = this.points, { minX, minY, invScaleX, invScaleY } = this, x = (i) => (points[i << 1] - minX) * invScaleX, y = (i) => (points[(i << 1) + 1] - minY) * invScaleY; // Determine if three points are in counter-clockwise order. const orient = (a, b, c) => { const ax = x(a), ay = y(a), bx = x(b) - ax, by = y(b) - ay, cx = x(c) - ax, cy = y(c) - ay, det = bx * cy - by * cx, err = (Math.abs(bx * cy) + Math.abs(by * cx)) * this.epsilon; return det > err; }; // Determine if a point (d) is inside the circumcircle of a triangle // (a, b, c). const inCircle = (a, b, c, d) => { if (a === d || b === d || c === d) { // Skip if d is one of the triangle vertices. return false; } const ax = x(a) - x(d), ay = y(a) - y(d), bx = x(b) - x(d), by = y(b) - y(d), cx = x(c) - x(d), cy = y(c) - y(d), aa = ax * ax + ay * ay, bb = bx * bx + by * by, cc = cx * cx + cy * cy, term1 = by * cc - bb * cy, term2 = bx * cc - bb * cx, term3 = bx * cy - by * cx, det = ax * term1 - ay * term2 + aa * term3, err = (Math.abs(ax * term1) + Math.abs(ay * term2) + Math.abs(aa * term3)) * this.epsilon; return det > err; }; // Data structures for the quad-edge data structure. let cap = Math.max(32, ((8 * count + 7) & ~3)), // Capacity (% 4 = 0) on = new Int32Array(cap), // Next edge in same face rt = new Int32Array(cap), // Rotation of edge (90 degrees) vtx = new Uint32Array(cap), // Origin vertex of edge seen = new Uint8Array(cap), // Visited flag for edge traversal top = 0; // Next free edge id (% 4 = 0) // Ensure the data structures have enough capacity for the required // number of edges. const ensure = (need) => { // If the capacity is sufficient, return. if (need <= cap) { return; } // Double capacity until sufficient. let ncap = cap << 1; while (ncap < need) { ncap <<= 1; } const on2 = new Int32Array(ncap), rt2 = new Int32Array(ncap), v2 = new Uint32Array(ncap), s2 = new Uint8Array(ncap); on2.set(on); rt2.set(rt); v2.set(vtx); s2.set(seen); on = on2; rt = rt2; vtx = v2; seen = s2; cap = ncap; }; const sym = (e) => rt[rt[e]], rotSym = (e) => sym(rt[e]), dest = (e) => vtx[sym(e)], lnext = (e) => rt[on[rotSym(e)]], oprev = (e) => rt[on[rt[e]]], rprev = (e) => on[sym(e)], leftOf = (p, e) => orient(p, vtx[e], dest(e)), rightOf = (p, e) => orient(p, dest(e), vtx[e]), admissible = (e, base) => rightOf(dest(e), base); // Create a new edge between two vertices. const makeEdge = (a, b) => { ensure(top + 4); const e0 = top, e1 = top + 1, e2 = top + 2, e3 = top + 3; top += 4; // Rot cycle rt[e0] = e1; rt[e1] = e2; rt[e2] = e3; rt[e3] = e0; // Onext initial on[e0] = e0; on[e2] = e2; on[e1] = e3; on[e3] = e1; // Origins vtx[e0] = a; vtx[e2] = b; vtx[e1] = 0xffffffff; vtx[e3] = 0xffffffff; return e0; }; // Splice two edges. const splice = (a, b) => { const alpha = rt[on[a]]; const beta = rt[on[b]]; const t2 = on[a]; const t3 = on[beta]; const t4 = on[alpha]; on[a] = on[b]; on[b] = t2; on[alpha] = t3; on[beta] = t4; }; // Connect two edges. const connect = (a, b) => { const q = makeEdge(dest(a), vtx[b]); splice(q, lnext(a)); splice(sym(q), b); return q; }; // Removes an edge from both sides. const drop = (e) => { splice(e, oprev(e)); const es = sym(e); splice(es, oprev(es)); }; const A = this.ids; // Recursively triangulate a range [lo, hi) of points. Returns the // two endpoints [left, right] of the lower common tangent. const solve = (lo, hi) => { const len = hi - lo; // If there are only two points, create a single edge. if (len === 2) { const a = makeEdge(A[lo], A[lo + 1]); return [a, sym(a)]; } // If there are three points, create two edges and connect them. if (len === 3) { const a = makeEdge(A[lo], A[lo + 1]), b = makeEdge(A[lo + 1], A[lo + 2]); splice(sym(a), b); const p0 = A[lo], p1 = A[lo + 1], p2 = A[lo + 2]; if (orient(p0, p1, p2)) { connect(b, a); return [a, sym(b)]; } if (orient(p0, p2, p1)) { const c = connect(b, a); return [sym(c), c]; } return [a, sym(b)]; } // Find the midpoint of the range. const mid = lo + ((len + 1) >>> 1); const L = solve(lo, mid); const R = solve(mid, hi); let ldo = L[0], ldi = L[1], rdi = R[0], rdo = R[1]; // Lower common tangent for (;;) { if (leftOf(vtx[rdi], ldi)) { ldi = lnext(ldi); } else if (rightOf(vtx[ldi], rdi)) { rdi = rprev(rdi); } else { break; } } let base = connect(sym(rdi), ldi); if (vtx[ldi] === vtx[ldo]) { ldo = sym(base); } if (vtx[rdi] === vtx[rdo]) { rdo = base; } // Merge loop - removing bad edges (inCircle) and adding new edges. for (;;) { // Left candidate let lc = on[sym(base)]; if (admissible(lc, base)) { while (inCircle(dest(base), vtx[base], dest(lc), dest(on[lc]))) { const t = on[lc]; drop(lc); lc = t; } } // Right candidate let rc = oprev(base); if (admissible(rc, base)) { while (inCircle(dest(base), vtx[base], dest(rc), dest(oprev(rc)))) { const t = oprev(rc); drop(rc); rc = t; } } if (!admissible(lc, base) && !admissible(rc, base)) { break; } if (!admissible(lc, base) || (admissible(rc, base) && inCircle(dest(lc), vtx[lc], vtx[rc], dest(rc)))) { base = connect(rc, sym(base)); } else { base = connect(sym(base), sym(lc)); } } return [ldo, rdo]; }; let e0 = solve(0, count)[0]; while (leftOf(dest(on[e0]), e0)) { e0 = on[e0]; } const Q = [e0]; let qi = 0; { let c = e0; do { Q.push(sym(c)); seen[c] = 1; c = lnext(c); } while (c !== e0); } const faces = []; let cur, t; while (qi < Q.length) { const e = Q[qi++]; if (seen[e]) { continue; } cur = e; do { faces.push(vtx[cur]); t = sym(cur); if (!seen[t]) { Q.push(t); } seen[cur] = 1; cur = lnext(cur); } while (cur !== e); } return new Uint32Array(faces); } } /* * * * Default Export * * */ /* harmony default export */ const Core_Delaunay = (Delaunay); ;// ./code/es-modules/Series/Contour/ContourSeries.js /* * * * (c) 2010-2026 Highsoft AS * Author: Torstein Hønsi * * Integration of this software requires a license. * - For commercial use, see www.highcharts.com/license * - For non-commercial, see www.highcharts.com/license-eula * * * */ /// <reference types="@webgpu/types" /> const { seriesTypes: { scatter: ScatterSeries } } = (external_highcharts_src_js_default_SeriesRegistry_default()); /* * * * Class * * */ /** @internal */ class ContourSeries extends ScatterSeries { static compose(SVGRendererClass) { Series_CrossSymbol.compose(SVGRendererClass); } /* Uniforms: * - extremesUniform, * - valueExtremesUniform, * - contourInterval, * - contourOffset, * - smoothColoring, * - lineWidth, * - contourLineColor * - colorAxisStops * - colorAxisStopsCount * - isInverted */ /* * * * Methods * * */ getContourData() { const points = this.points, len = points.length, points3d = new Float32Array(len * 3), points2d = new Float64Array(len * 2); for (let i = 0; i < len; i++) { const { x, y = 0, value } = points[i], index2d = i * 2, index3d = i * 3; points2d[index2d] = x; points2d[index2d + 1] = y; points3d[index3d] = x; points3d[index3d + 1] = y; points3d[index3d + 2] = value ?? 0; } return [new Core_Delaunay(points2d).triangles, points3d]; } update(options, redraw) { options = (0,external_highcharts_src_js_default_namespaceObject.diffObjects)(options, this.userOptions); const uniformOptions = [ 'smoothColoring', 'contourInterval', 'contourOffset', 'lineColor', 'lineWidth' ]; const isUniformOption = (key) => (uniformOptions.includes(key)); const hasNonUniformOptions = Object.keys(options).some((key) => !isUniformOption(key)); // Only fetch plotOptions if all options are uniform related. const allOptions = (hasNonUniformOptions ? void 0 : this.setOptions((0,external_highcharts_src_js_default_namespaceObject.merge)(this.userOptions, options))), hasNonUniformPlotOptions = allOptions ? Object.keys((0,external_highcharts_src_js_default_namespaceObject.diffObjects)(allOptions, this.options)).some((key) => !isUniformOption(key)) : false; if (hasNonUniformOptions || hasNonUniformPlotOptions) { super.update(options, redraw); } else { this.options = allOptions; // If only uniform-related options changed, avoid full series // reconstruction and update uniforms only. this.setUniforms(); } } drawPoints() { const { group } = this; if (!group) { return; } if (!this.canvas) { this.foreignObject = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject'); group.element.appendChild(this.foreignObject); this.canvas = document.createElement('canvas'); this.foreignObject.appendChild(this.canvas); } const { canvas, xAxis, yAxis } = this, foreignObject = this.foreignObject, oldWidth = foreignObject.width.baseVal.value, oldHeight = foreignObject.height.baseVal.value, { devicePixelRatio: dpr } = window; let width = xAxis.len, height = yAxis.len; if (this.chart.inverted) { [width, height] = [height, width]; } if (oldWidth !== width) { foreignObject.setAttribute('width', width); canvas.width = width * dpr; canvas.style.width = width + 'px'; } if (oldHeight !== height) { foreignObject.setAttribute('height', height); canvas.height = height * dpr; canvas.style.height = height + 'px'; } if (this.renderFrame) { this.renderFrame(); } else { /* eslint-disable @typescript-eslint/no-floating-promises */ this.run(); } } async run() { const series = this, chart = series.chart, renderer = chart.renderer, canvas = series.canvas, gpu = navigator.gpu, context = series.context = canvas.getContext('webgpu'); if (!gpu || !context) { (0,external_highcharts_src_js_default_namespaceObject.error)(37, false, chart); return; } renderer.asyncCounter += 1; if (context) { let device = this.device; if (!this.adapter) { this.adapter = await gpu.requestAdapter(); } if (!device && this.adapter) { device = this.device = await this.adapter.requestDevice(); } const canvasFormat = gpu.getPreferredCanvasFormat(); if (device) { context.configure({ device: device, format: canvasFormat, colorSpace: 'display-p3', alphaMode: 'premultiplied', usage: (GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC) }); const [indices, vertices] = this.getContourData(); // WebGPU Buffers grouped under a single object const buffers = this.buffers = { vertex: device.createBuffer({ size: vertices.byteLength, usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST }), index: device.createBuffer({ size: indices.byteLength, usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST }), extremesUniform: device.createBuffer({ size: Float32Array.BYTES_PER_ELEMENT * 4, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }), valueExtremesUniform: device.createBuffer({ size: Float32Array.BYTES_PER_ELEMENT * 2, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }), contourIntervalUniform: device.createBuffer({ size: 4, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }), contourOffsetUniform: device.createBuffer({ size: 4, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }), smoothColoringUniform: device.createBuffer({ size: 4, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }), lineWidthUniform: device.createBuffer({ size: 4, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }), contourLineColor: device.createBuffer({ size: 12, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }), colorAxisStopsCountUniform: device.createBuffer({ size: 4, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }), colorAxisStopsUniform: device.createBuffer({ size: Float32Array.BYTES_PER_ELEMENT * 64, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST }), isInvertedUniform: device.createBuffer({ size: 4, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }) }; const { vertex: vertexBuffer, index: indexBuffer, extremesUniform: extremesUniformBuffer, valueExtremesUniform: valueExtremesUniformBuffer, contourIntervalUniform: contourIntervalUniformBuffer, contourOffsetUniform: contourOffsetUniformBuffer, smoothColoringUniform: smoothColoringUniformBuffer, lineWidthUniform: lineWidthUniformBuffer, contourLineColor: contourLineColorBuffer, colorAxisStopsCountUniform: colAxisStopsCountUniformBuffer, colorAxisStopsUniform: colorAxisStopsUniformBuffer, isInvertedUniform: isInvertedUniformBuffer } = buffers; device.queue.writeBuffer(vertexBuffer, 0, vertices); device.queue.writeBuffer(indexBuffer, 0, indices); const vertexBufferLayout = { arrayStride: 12, attributes: [{ format: 'float32x3', offset: 0, shaderLocation: 0 }] }; const shaderModule = device.createShaderModule({ code: Contour_ContourShader }); const pipeline = device.createRenderPipeline({ layout: 'auto', vertex: { module: shaderModule, entryPoint: 'vertexMain', buffers: [vertexBufferLayout] }, fragment: { module: shaderModule, entryPoint: 'fragmentMain', targets: [{ format: canvasFormat }] }, primitive: { topology: 'triangle-list' } }); const bindGroup = device.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [{ binding: 0, resource: { buffer: extremesUniformBuffer, label: 'extremesUniformBuffer' } }, { binding: 1, resource: { buffer: valueExtremesUniformBuffer, label: 'valueExtremesUniformBuffer' } }, { binding: 2, resource: { buffer: colorAxisStopsUniformBuffer, label: 'colorAxisStopsBuffer' } }, { binding: 3, resource: { buffer: colAxisStopsCountUniformBuffer, label: 'colorAxisStopsCountBuffer' } }, { binding: 4, resource: { buffer: contourIntervalUniformBuffer, label: 'contourIntervalUniformBuffer' } }, { binding: 5, resource: { buffer: contourOffsetUniformBuffer, label: 'contourOffsetUniformBuffer' } }, { binding: 6, resource: { buffer: smoothColoringUniformBuffer, label: 'smoothColoringUniformBuffer' } }, { binding: 7, resource: { buffer: lineWidthUniformBuffer, label: 'lineWidthUniformBuffer' } }, { binding: 8, resource: { buffer: contourLineColorBuffer, label: 'contourLineColorBuffer' } }, { binding: 9, resource: { buffer: isInvertedUniformBuffer, label: 'isInvertedUniformBuffer' } }] }); this.renderFrame = function () { this.setUniforms(false); const encoder = device.createCommandEncoder(), currentTexture = context.getCurrentTexture(), pass = encoder.beginRenderPass({ colorAttachments: [{ view: currentTexture.createView(), loadOp: 'clear', clearValue: [0, 0, 0, 0], storeOp: 'store' }] }); pass.setPipeline(pipeline); pass.setVertexBuffer(0, vertexBuffer); pass.setIndexBuffer(indexBuffer, 'uint32'); pass.setBindGroup(0, bindGroup); pass.drawIndexed(indices.length); pass.end(); device.queue.submit([encoder.finish()]); }; this.renderFrame(); } } renderer.asyncCounter--; if (!renderer.asyncCounter && chart && !chart.hasLoaded) { chart.onload(); } } destroy() { // Remove the foreign object. The canvas will be removed with it. // For some reason, `series.update` calls `series.destroy` even if // update does not trigger a rerender. This causes the canvas to be // removed here (unnecessarily) and that causes the flickering effect // when updating. this.canvas?.parentElement?.remove(); super.destroy(); } drawGraph() { // Do nothing } /** * Set all the updateable uniforms. * * @param {boolean} renderFrame * Whether to rerender the series' context after setting the uniforms. * Defaults to `true`. */ setUniforms(renderFrame = true) { this.setFrameExtremesUniform(false); this.setValueExtremesUniform(false); this.setColorAxisStopsUniforms(false); this.setContourIntervalUniform(false); this.setContourOffsetUniform(false); this.setSmoothColoringUniform(false); this.setLineWidthUniform(false); this.setContourLineColorUniform(false); this.setIsInvertedUniform(renderFrame); } /** * Set the contour interval uniform according to the series options. * * @param {boolean} renderFrame * Whether to rerender the series' context after setting the uniform. * Defaults to `true`. */ setContourIntervalUniform(renderFrame = true) { if (this.device && this.buffers?.contourIntervalUniform) { this.device.queue.writeBuffer(this.buffers.contourIntervalUniform, 0, new Float32Array([this.getContourInterval()])); if (renderFrame) { this.renderFrame?.(); } } } /** * Set the contour offset uniform according to the series options. */ setContourOffsetUniform(renderFrame = true) { if (this.device && this.buffers?.contourOffsetUniform) { this.device.queue.writeBuffer(this.buffers.contourOffsetUniform, 0, new Float32Array([this.getContourOffset()])); if (renderFrame) { this.renderFrame?.(); } } } /** * Set the smooth coloring uniform according to the series options. */ setSmoothColoringUniform(renderFrame = true) { if (this.device && this.buffers?.smoothColoringUniform) { this.device.queue.writeBuffer(this.buffers.smoothColoringUniform, 0, new Float32Array([this.getSmoothColoring()])); if (renderFrame) { this.renderFrame?.(); } } } /** * Set the line width uniform according to the series options. */ setLineWidthUniform(renderFrame = true) { if (this.device && this.buffers?.lineWidthUniform) { this.device.queue.writeBuffer(this.buffers.lineWidthUniform, 0, new Float32Array([this.getLineWidth()])); if (renderFrame) { this.renderFrame?.(); } } } /** * Set the contour line color uniform according to the series options. */ setContourLineColorUniform(renderFrame = true) { if (this.device && this.buffers?.contourLineColor) { this.device.queue.writeBuffer(this.buffers.contourLineColor, 0, new Float32Array(this.getContourLineColor())); if (renderFrame) { this.renderFrame?.(); } } } /** * Set the frame extremes uniform according to the series options. */ setFrameExtremesUniform(renderFrame = true) { if (this.device && this.buffers?.extremesUniform) { this.device.queue.writeBuffer(this.buffers.extremesUniform, 0, new Float32Array(this.getFrameExtremes())); if (renderFrame) { this.renderFrame?.(); } } } /** * Set the value extremes uniform according to the series data. */ setValueExtremesUniform(renderFrame = true) { if (this.device && this.buffers?.valueExtremesUniform) { this.device.queue.writeBuffer(this.buffers.valueExtremesUniform, 0, new Float32Array(this.getValueAxisExtremes())); if (renderFrame) { this.renderFrame?.(); } } } /** * Set the color axis stops uniforms according to the color axis options. */ setColorAxisStopsUniforms(renderFrame = true) { const stopsBuffer = this.buffers?.colorAxisStopsUniform; const countBuffer = this.buffers?.colorAxisStopsCountUniform; if (this.device && stopsBuffer && countBuffer) { const { array, length } = this.getColorAxisStopsData(); // Write the stops to the buffer this.device.queue.writeBuffer(stopsBuffer, 0, array); // Write the count to the buffer this.device.queue.writeBuffer(countBuffer, 0, new Uint32Array([length])); if (renderFrame) { this.renderFrame?.(); } } } /** * Set the is inverted uniform according to the series options. */ setIsInvertedUniform(renderFrame = true) { if (this.device && this.buffers?.isInvertedUniform) { this.device.queue.writeBuffer(this.buffers.isInvertedUniform, 0, new Uint32Array([this.chart.inverted ? 1 : 0])); if (renderFrame) { this.renderFrame?.(); } } } /** * Returns the contour interval from the series options in format of the * WebGPU uniform. */ getContourInterval() { const interval = this.options.contourInterval ?? (() => { const [min, max] = this.getValueAxisExtremes(), range = max - min; return (0,external_highcharts_src_js_default_namespaceObject.normalizeTickInterval)(range / 10); })(); if (isNaN(interval) || interval <= 0) { return -1; } return interval; } /** * Returns the contour offset from the series options in format of the * WebGPU uniform. */ getContourOffset() { const offset = this.options.contourOffset ?? 0; if (isNaN(offset) || offset <= 0) { return 0; } return offset; } /** * Returns the smooth coloring from the series options in format of the * WebGPU uniform. */ getSmoothColoring() { return this.options.smoothColoring ? 1 : 0; } /** * Returns the lineWidth from the series options, which controls the * visibility of contour lines, in format of the WebGPU uniform. */ getLineWidth() { return this.userOptions.lineWidth ?? 1; } /** * Returns the contour line color from the series options in format of the * WebGPU uniform. */ getContourLineColor() { const { lineColor = '#000000' } = this.options; return ContourSeries.rgbaAsFrac(new (external_highcharts_src_js_default_Color_default())(lineColor).rgba); } /** * Returns the extremes of the x and y axes in format of the WebGPU uniform. */ getFrameExtremes() { const { xAxis, yAxis } = this; return [ xAxis.toValue(0, true), xAxis.toValue(xAxis.len, true), yAxis.toValue(yAxis.len, true), yAxis