"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var __decorateClass = (decorators, target, key, kind) => { var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result; if (kind && result) __defProp(target, key, result); return result; }; // packages/ag-charts-community/src/integrated-charts-scene.ts var integrated_charts_scene_exports = {}; __export(integrated_charts_scene_exports, { Arc: () => Arc, BBox: () => BBox, Caption: () => Caption, CategoryScale: () => CategoryScale, Group: () => Group, Line: () => Line, LinearScale: () => LinearScale, Marker: () => Marker, Path: () => Path, RadialColumnShape: () => RadialColumnShape, Rect: () => Rect, Scene: () => Scene, Sector: () => Sector, Shape: () => Shape, TranslatableGroup: () => TranslatableGroup, getRadialColumnWidth: () => getRadialColumnWidth, toRadians: () => toRadians }); module.exports = __toCommonJS(integrated_charts_scene_exports); // packages/ag-charts-core/src/globals/logger.ts var logger_exports = {}; __export(logger_exports, { error: () => error, errorOnce: () => errorOnce, log: () => log, logGroup: () => logGroup, reset: () => reset, table: () => table, warn: () => warn, warnOnce: () => warnOnce }); var doOnceCache = /* @__PURE__ */ new Set(); function log(...logContent) { console.log(...logContent); } function warn(message, ...logContent) { console.warn(`AG Charts - ${message}`, ...logContent); } function error(message, ...logContent) { if (typeof message === "object") { console.error(`AG Charts error`, message, ...logContent); } else { console.error(`AG Charts - ${message}`, ...logContent); } } function table(...logContent) { console.table(...logContent); } function warnOnce(message, ...logContent) { const cacheKey = `Logger.warn: ${message}`; if (doOnceCache.has(cacheKey)) return; warn(message, ...logContent); doOnceCache.add(cacheKey); } function errorOnce(message, ...logContent) { const cacheKey = `Logger.error: ${message}`; if (doOnceCache.has(cacheKey)) return; error(message, ...logContent); doOnceCache.add(cacheKey); } function reset() { doOnceCache.clear(); } function logGroup(name, cb) { console.groupCollapsed(name); try { return cb(); } finally { console.groupEnd(); } } // packages/ag-charts-core/src/utils/arrays.ts function toArray(value) { if (typeof value === "undefined") { return []; } return Array.isArray(value) ? value : [value]; } // packages/ag-charts-core/src/utils/iterators.ts function toIterable(value) { return value != null && typeof value === "object" && Symbol.iterator in value ? value : [value]; } // packages/ag-charts-core/src/utils/strings.ts function stringifyValue(value, maxLength = Infinity) { switch (typeof value) { case "undefined": return "undefined"; case "number": if (isNaN(value)) { return "NaN"; } else if (value === Infinity) { return "Infinity"; } else if (value === -Infinity) { return "-Infinity"; } default: value = JSON.stringify(value); if (value.length > maxLength) { return `${value.slice(0, maxLength)}... (+${value.length - maxLength} characters)`; } return value; } } // packages/ag-charts-core/src/utils/typeGuards.ts function isArray(value) { return Array.isArray(value); } function isBoolean(value) { return typeof value === "boolean"; } function isDate(value) { return value instanceof Date; } function isValidDate(value) { return isDate(value) && !isNaN(Number(value)); } function isFunction(value) { return typeof value === "function"; } function isObject(value) { return typeof value === "object" && value !== null && !isArray(value); } function isString(value) { return typeof value === "string"; } function isNumber(value) { return typeof value === "number"; } function isFiniteNumber(value) { return Number.isFinite(value); } // packages/ag-charts-community/src/core/globalsProxy.ts var verifiedGlobals = {}; if (typeof window !== "undefined") { verifiedGlobals.window = window; } else if (typeof global !== "undefined") { verifiedGlobals.window = global.window; } if (typeof document !== "undefined") { verifiedGlobals.document = document; } else if (typeof global !== "undefined") { verifiedGlobals.document = global.document; } function getDocument(propertyName) { return propertyName ? verifiedGlobals.document?.[propertyName] : verifiedGlobals.document; } function getWindow(propertyName) { return propertyName ? verifiedGlobals.window?.[propertyName] : verifiedGlobals.window; } // packages/ag-charts-community/src/core/domElements.ts function createElement(tagName, className, style) { const element = getDocument().createElement(tagName); if (typeof className === "object") { style = className; className = void 0; } if (className) { for (const name of className.split(" ")) { element.classList.add(name); } } if (style) { Object.assign(element.style, style); } return element; } function createSvgElement(elementName) { return getDocument().createElementNS("http://www.w3.org/2000/svg", elementName); } // packages/ag-charts-community/src/core/domDownload.ts function downloadUrl(dataUrl, fileName) { const body = getDocument("body"); const element = createElement("a", { display: "none" }); element.href = dataUrl; element.download = fileName; body.appendChild(element); element.click(); setTimeout(() => body.removeChild(element)); } // packages/ag-charts-community/src/util/id.ts var ID_MAP = /* @__PURE__ */ new Map(); function createId(instance) { const constructor = instance.constructor; const className = Object.hasOwn(constructor, "className") ? constructor.className : constructor.name; if (!className) { throw new Error(`The ${constructor} is missing the 'className' property.`); } const nextId = (ID_MAP.get(className) ?? 0) + 1; ID_MAP.set(className, nextId); return `${className}-${nextId}`; } // packages/ag-charts-community/src/util/bboxinterface.ts var BBoxValues = { containsPoint, isEmpty, normalize }; function containsPoint(bbox, x, y) { return x >= bbox.x && x <= bbox.x + bbox.width && y >= bbox.y && y <= bbox.y + bbox.height; } function isEmpty(bbox) { return bbox == null || bbox.height === 0 || bbox.width === 0 || isNaN(bbox.height) || isNaN(bbox.width); } function normalize(bbox) { let { x, y, width, height } = bbox; if ((width == null || width > 0) && (height == null || height > 0)) return bbox; if (x != null && width != null && width < 0) { width = -width; x = x - width; } if (y != null && height != null && height < 0) { height = -height; y = y - height; } return { x, y, width, height }; } // packages/ag-charts-community/src/util/interpolating.ts var interpolate = Symbol("interpolate"); // packages/ag-charts-community/src/util/nearest.ts function nearestSquared(x, y, objects, maxDistanceSquared = Infinity) { const result = { nearest: void 0, distanceSquared: maxDistanceSquared }; for (const obj of objects) { const thisDistance = obj.distanceSquared(x, y); if (thisDistance === 0) { return { nearest: obj, distanceSquared: 0 }; } else if (thisDistance < result.distanceSquared) { result.nearest = obj; result.distanceSquared = thisDistance; } } return result; } // packages/ag-charts-community/src/util/number.ts function clamp(min, value, max) { return Math.min(max, Math.max(min, value)); } function findMinMax(array) { if (array.length === 0) return []; const result = [Infinity, -Infinity]; for (const val of array) { if (val < result[0]) result[0] = val; if (val > result[1]) result[1] = val; } return result; } function isNumberEqual(a, b, epsilon2 = 1e-10) { return a === b || Math.abs(a - b) < epsilon2; } function countFractionDigits(value) { if (Math.floor(value) === value) return 0; let valueString = String(value); let exponent = 0; if (value < 1e-6 || value >= 1e21) { let exponentString; [valueString, exponentString] = valueString.split("e"); if (exponentString != null) { exponent = Number(exponentString); } } const decimalPlaces2 = valueString.split(".")[1]?.length ?? 0; return Math.max(decimalPlaces2 - exponent, 0); } // packages/ag-charts-community/src/scene/bbox.ts var _BBox = class _BBox { constructor(x, y, width, height) { this.x = x; this.y = y; this.width = width; this.height = height; } static fromDOMRect({ x, y, width, height }) { return new _BBox(x, y, width, height); } static merge(boxes) { let left = Infinity; let top = Infinity; let right = -Infinity; let bottom = -Infinity; for (const box of boxes) { if (box.x < left) { left = box.x; } if (box.y < top) { top = box.y; } if (box.x + box.width > right) { right = box.x + box.width; } if (box.y + box.height > bottom) { bottom = box.y + box.height; } } return new _BBox(left, top, right - left, bottom - top); } static nearestBox(x, y, boxes) { return nearestSquared(x, y, boxes); } toDOMRect() { return { x: this.x, y: this.y, width: this.width, height: this.height, top: this.y, left: this.x, right: this.x + this.width, bottom: this.y + this.height, toJSON() { return {}; } }; } clone() { const { x, y, width, height } = this; return new _BBox(x, y, width, height); } equals(other) { return this.x === other.x && this.y === other.y && this.width === other.width && this.height === other.height; } containsPoint(x, y) { return BBoxValues.containsPoint(this, x, y); } intersection(other) { if (!this.collidesBBox(other)) return; const newX1 = clamp(other.x, this.x, other.x + other.width); const newY1 = clamp(other.y, this.y, other.y + other.height); const newX2 = clamp(other.x, this.x + this.width, other.x + other.width); const newY2 = clamp(other.y, this.y + this.height, other.y + other.height); return new _BBox(newX1, newY1, newX2 - newX1, newY2 - newY1); } collidesBBox(other) { return this.x < other.x + other.width && this.x + this.width > other.x && this.y < other.y + other.height && this.y + this.height > other.y; } computeCenter() { return { x: this.x + this.width / 2, y: this.y + this.height / 2 }; } isFinite() { return Number.isFinite(this.x) && Number.isFinite(this.y) && Number.isFinite(this.width) && Number.isFinite(this.height); } distanceSquared(x, y) { if (this.containsPoint(x, y)) { return 0; } const dx = x - clamp(this.x, x, this.x + this.width); const dy = y - clamp(this.y, y, this.y + this.height); return dx * dx + dy * dy; } shrink(amount, position) { if (typeof amount === "number") { this.applyMargin(amount, position); } else { for (const [key, value] of Object.entries(amount)) { this.applyMargin(value, key); } } if (this.width < 0) { this.width = 0; } if (this.height < 0) { this.height = 0; } return this; } grow(amount, position) { if (typeof amount === "number") { this.applyMargin(-amount, position); } else { for (const [key, value] of Object.entries(amount)) { this.applyMargin(-value, key); } } return this; } applyMargin(value, position) { switch (position) { case "top": this.y += value; case "bottom": this.height -= value; break; case "left": this.x += value; case "right": this.width -= value; break; case "vertical": this.y += value; this.height -= value * 2; break; case "horizontal": this.x += value; this.width -= value * 2; break; case void 0: this.x += value; this.y += value; this.width -= value * 2; this.height -= value * 2; break; } } translate(x, y) { this.x += x; this.y += y; return this; } [interpolate](other, d) { return new _BBox( this.x * (1 - d) + other.x * d, this.y * (1 - d) + other.y * d, this.width * (1 - d) + other.width * d, this.height * (1 - d) + other.height * d ); } }; _BBox.zero = Object.freeze(new _BBox(0, 0, 0, 0)); _BBox.NaN = Object.freeze(new _BBox(NaN, NaN, NaN, NaN)); var BBox = _BBox; // packages/ag-charts-community/src/scene/changeDetectable.ts function SceneChangeDetection(opts) { return function(target, key) { const privateKey = `__${key}`; if (target[key]) { return; } prepareGetSet(target, key, privateKey, opts); }; } function prepareGetSet(target, key, privateKey, opts) { const { type = "normal", changeCb, convertor, checkDirtyOnAssignment = false } = opts ?? {}; const requiredOpts = { type, changeCb, checkDirtyOnAssignment, convertor }; let setter; switch (type) { case "normal": setter = buildNormalSetter(privateKey, requiredOpts); break; case "transform": setter = buildTransformSetter(privateKey); break; case "path": setter = buildPathSetter(privateKey); break; } setter = buildCheckDirtyChain( buildChangeCallbackChain(buildConvertorChain(setter, requiredOpts), requiredOpts), requiredOpts ); const getter = function() { return this[privateKey]; }; Object.defineProperty(target, key, { set: setter, get: getter, enumerable: true, configurable: true }); } function buildConvertorChain(setterFn, opts) { const { convertor } = opts; if (convertor) { return function(value) { setterFn.call(this, convertor(value)); }; } return setterFn; } var NO_CHANGE = Symbol("no-change"); function buildChangeCallbackChain(setterFn, opts) { const { changeCb } = opts; if (changeCb) { return function(value) { const change = setterFn.call(this, value); if (change !== NO_CHANGE) { changeCb.call(this, this); } return change; }; } return setterFn; } function buildCheckDirtyChain(setterFn, opts) { const { checkDirtyOnAssignment } = opts; if (checkDirtyOnAssignment) { return function(value) { const change = setterFn.call(this, value); if (value?._dirty === true) { this.markDirty(); } return change; }; } return setterFn; } function buildNormalSetter(privateKey, opts) { const { changeCb } = opts; return function(value) { const oldValue = this[privateKey]; if (value !== oldValue) { this[privateKey] = value; this.markDirty(); changeCb?.(this); return value; } return NO_CHANGE; }; } function buildTransformSetter(privateKey) { return function(value) { const oldValue = this[privateKey]; if (value !== oldValue) { this[privateKey] = value; this.markDirtyTransform(); return value; } return NO_CHANGE; }; } function buildPathSetter(privateKey) { return function(value) { const oldValue = this[privateKey]; if (value !== oldValue) { this[privateKey] = value; if (!this._dirtyPath) { this._dirtyPath = true; this.markDirty(); } return value; } return NO_CHANGE; }; } // packages/ag-charts-community/src/scene/node.ts var _Node = class _Node { constructor(options) { /** Unique number to allow creation order to be easily determined. */ this.serialNumber = _Node._nextSerialNumber++; this.childNodeCounts = { groups: 0, nonGroups: 0, thisComplexity: 0, complexity: 0 }; /** Unique node ID in the form `ClassName-NaturalNumber`. */ this.id = createId(this); this.pointerEvents = 0 /* All */; this._dirty = true; this.dirtyZIndex = false; /** * To simplify the type system (especially in Selections) we don't have the `Parent` node * (one that has children). Instead, we mimic HTML DOM, where any node can have children. * But we still need to distinguish regular leaf nodes from container leafs somehow. */ this.isContainerNode = false; this.visible = true; this.zIndex = 0; this.name = options?.name; this.tag = options?.tag ?? NaN; this.zIndex = options?.zIndex ?? 0; } static toSVG(node, width, height) { const svg = node?.toSVG(); if (svg == null || !svg.elements.length && !svg.defs?.length) return; const root = createSvgElement("svg"); root.setAttribute("width", String(width)); root.setAttribute("height", String(height)); root.setAttribute("viewBox", `0 0 ${width} ${height}`); if (svg.defs?.length) { const defs = createSvgElement("defs"); defs.append(...svg.defs); root.append(defs); } root.append(...svg.elements); return root.outerHTML; } static *extractBBoxes(nodes, skipInvisible) { for (const n of nodes) { if (!skipInvisible || n.visible && !n.transitionOut) { const bbox = n.getBBox(); if (bbox) yield bbox; } } } /** * Some arbitrary data bound to the node. */ get datum() { return this._datum ?? this.parentNode?.datum; } set datum(datum) { if (this._datum !== datum) { this._previousDatum = this._datum; this._datum = datum; } } get previousDatum() { return this._previousDatum; } get layerManager() { return this._layerManager; } get dirty() { return this._dirty; } /** Perform any pre-rendering initialization. */ preRender(renderCtx, thisComplexity = 1) { this.childNodeCounts.groups = 0; this.childNodeCounts.nonGroups = 1; this.childNodeCounts.complexity = thisComplexity; this.childNodeCounts.thisComplexity = thisComplexity; for (const child of this.children()) { const childCounts = child.preRender(renderCtx); this.childNodeCounts.groups += childCounts.groups; this.childNodeCounts.nonGroups += childCounts.nonGroups; this.childNodeCounts.complexity += childCounts.complexity; } return this.childNodeCounts; } render(renderCtx) { const { stats } = renderCtx; this._dirty = false; if (renderCtx.debugNodeSearch) { const idOrName = this.name ?? this.id; if (renderCtx.debugNodeSearch.some((v) => typeof v === "string" ? v === idOrName : v.test(idOrName))) { renderCtx.debugNodes[this.name ?? this.id] = this; } } if (stats) { stats.nodesRendered++; stats.opsPerformed += this.childNodeCounts.thisComplexity; } } _setLayerManager(value) { this._layerManager = value; this._debug = value?.debug; for (const child of this.children()) { child._setLayerManager(value); } } sortChildren(compareFn) { this.dirtyZIndex = false; if (!this.childNodes) return; const sortedChildren = [...this.childNodes].sort(compareFn); this.childNodes.clear(); for (const child of sortedChildren) { this.childNodes.add(child); } } *traverseUp(includeSelf) { let node = this; if (includeSelf) { yield node; } while (node = node.parentNode) { yield node; } } *children() { if (!this.childNodes) return; for (const child of this.childNodes) { yield child; } } *descendants() { for (const child of this.children()) { yield child; yield* child.descendants(); } } /** * Checks if the node is a leaf (has no children). */ isLeaf() { return !this.childNodes?.size; } /** * Checks if the node is the root (has no parent). */ isRoot() { return !this.parentNode; } /** * Appends one or more new node instances to this parent. * If one needs to: * - move a child to the end of the list of children * - move a child from one parent to another (including parents in other scenes) * one should use the {@link insertBefore} method instead. * @param nodes A node or nodes to append. */ append(nodes) { this.childNodes ?? (this.childNodes = /* @__PURE__ */ new Set()); for (const node of toIterable(nodes)) { node.parentNode?.removeChild(node); this.childNodes.add(node); node.parentNode = this; node._setLayerManager(this.layerManager); } this.invalidateCachedBBox(); this.dirtyZIndex = true; this.markDirty(); } appendChild(node) { this.append(node); return node; } removeChild(node) { if (!this.childNodes?.delete(node)) { return false; } delete node.parentNode; node._setLayerManager(); this.invalidateCachedBBox(); this.dirtyZIndex = true; this.markDirty(); return true; } remove() { return this.parentNode?.removeChild(this) ?? false; } clear() { for (const child of this.children()) { delete child.parentNode; child._setLayerManager(); } this.childNodes?.clear(); this.invalidateCachedBBox(); } destroy() { this.parentNode?.removeChild(this); } setProperties(styles, pickKeys) { if (pickKeys) { for (const key of pickKeys) { this[key] = styles[key]; } } else { Object.assign(this, styles); } return this; } containsPoint(_x, _y) { return false; } /** * Hit testing method. * Recursively checks if the given point is inside this node or any of its children. * Returns the first matching node or `undefined`. * Nodes that render later (show on top) are hit tested first. */ pickNode(x, y, _localCoords = false) { if (!this.visible || this.pointerEvents === 1 /* None */ || !this.containsPoint(x, y)) { return; } const children = [...this.children()]; if (children.length > 1e3) { for (let i = children.length - 1; i >= 0; i--) { const child = children[i]; const containsPoint2 = child.containsPoint(x, y); const hit = containsPoint2 ? child.pickNode(x, y) : void 0; if (hit) { return hit; } } } else if (children.length) { for (let i = children.length - 1; i >= 0; i--) { const hit = children[i].pickNode(x, y); if (hit) { return hit; } } } else if (!this.isContainerNode) { return this; } } invalidateCachedBBox() { if (this.cachedBBox != null) { this.cachedBBox = void 0; this.parentNode?.invalidateCachedBBox(); } } getBBox() { if (this.cachedBBox == null) { this.cachedBBox = Object.freeze(this.computeBBox()); } return this.cachedBBox; } computeBBox() { return; } markDirty() { const { _dirty } = this; const noParentCachedBBox = this.cachedBBox == null; if (noParentCachedBBox && _dirty) return; this.invalidateCachedBBox(); this._dirty = true; if (this.parentNode) { this.parentNode.markDirty(); } } markClean() { if (!this._dirty) return; this._dirty = false; for (const child of this.children()) { child.markClean(); } } onZIndexChange() { const { parentNode } = this; if (parentNode) { parentNode.dirtyZIndex = true; } } toSVG() { return; } }; _Node._nextSerialNumber = 0; __decorateClass([ SceneChangeDetection() ], _Node.prototype, "visible", 2); __decorateClass([ SceneChangeDetection({ changeCb: (target) => target.onZIndexChange() }) ], _Node.prototype, "zIndex", 2); var Node = _Node; // packages/ag-charts-community/src/util/canvas.util.ts function createCanvasContext(width = 0, height = 0) { return new OffscreenCanvas(width, height).getContext("2d"); } // packages/ag-charts-community/src/util/lruCache.ts var LRUCache = class { constructor(maxCacheSize = 5) { this.maxCacheSize = maxCacheSize; this.store = /* @__PURE__ */ new Map(); } get(key) { if (!this.store.has(key)) return void 0; const hit = this.store.get(key); this.store.delete(key); this.store.set(key, hit); return hit; } has(key) { return this.store.has(key); } set(key, value) { this.store.set(key, value); if (this.store.size > this.maxCacheSize) { const iterator = this.store.keys(); let evictCount = this.store.size - this.maxCacheSize; while (evictCount > 0) { const evictKeyIterator = iterator.next(); if (!evictKeyIterator.done) { this.store.delete(evictKeyIterator.value); } evictCount--; } } return value; } clear() { this.store.clear(); } }; // packages/ag-charts-community/src/util/textMeasurer.ts var CachedTextMeasurerPool = class { // Measures the dimensions of the provided text, handling multiline if needed. static measureText(text, options) { const textMeasurer = this.getMeasurer(options); return textMeasurer.measureText(text); } static measureLines(text, options) { const textMeasurer = this.getMeasurer(options); return textMeasurer.measureLines(text); } // Gets a TextMeasurer instance, configuring text alignment and baseline if provided. static getMeasurer(options) { const font = typeof options.font === "string" ? options.font : TextUtils.toFontString(options.font); const key = `${font}-${options.textAlign ?? "start"}-${options.textBaseline ?? "alphabetic"}`; return this.instanceMap.get(key) ?? this.createFontMeasurer(font, options, key); } // Creates or retrieves a TextMeasurer instance for a specific font. static createFontMeasurer(font, options, key) { const ctx = createCanvasContext(); ctx.font = font; ctx.textAlign = options.textAlign ?? "start"; ctx.textBaseline = options.textBaseline ?? "alphabetic"; const measurer = new CachedTextMeasurer(ctx, options); this.instanceMap.set(key, measurer); return measurer; } }; CachedTextMeasurerPool.instanceMap = new LRUCache(10); var CachedTextMeasurer = class { constructor(ctx, options) { this.ctx = ctx; // cached text measurements this.measureMap = new LRUCache(100); if (options.textAlign) { ctx.textAlign = options.textAlign; } if (options.textBaseline) { ctx.textBaseline = options.textBaseline; } ctx.font = typeof options.font === "string" ? options.font : TextUtils.toFontString(options.font); this.textMeasurer = new SimpleTextMeasurer( (t) => this.cachedCtxMeasureText(t), options.textBaseline ?? "alphabetic" ); } textWidth(text, estimate) { return this.textMeasurer.textWidth(text, estimate); } measureText(text) { return this.textMeasurer.measureText(text); } measureLines(text) { return this.textMeasurer.measureLines(text); } cachedCtxMeasureText(text) { if (!this.measureMap.has(text)) { const rawResult = this.ctx.measureText(text); this.measureMap.set(text, { actualBoundingBoxAscent: rawResult.actualBoundingBoxAscent, emHeightAscent: rawResult.emHeightAscent, emHeightDescent: rawResult.emHeightDescent, actualBoundingBoxDescent: rawResult.actualBoundingBoxDescent, actualBoundingBoxLeft: rawResult.actualBoundingBoxLeft, actualBoundingBoxRight: rawResult.actualBoundingBoxRight, alphabeticBaseline: rawResult.alphabeticBaseline, fontBoundingBoxAscent: rawResult.fontBoundingBoxAscent, fontBoundingBoxDescent: rawResult.fontBoundingBoxDescent, hangingBaseline: rawResult.hangingBaseline, ideographicBaseline: rawResult.ideographicBaseline, width: rawResult.width }); } return this.measureMap.get(text); } }; var TextUtils = class { static toFontString({ fontSize = 10, fontStyle, fontWeight, fontFamily, lineHeight }) { let fontString = ""; if (fontStyle) { fontString += `${fontStyle} `; } if (fontWeight) { fontString += `${fontWeight} `; } fontString += `${fontSize}px`; if (lineHeight) { fontString += `/${lineHeight}px`; } fontString += ` ${fontFamily}`; return fontString.trim(); } static getLineHeight(fontSize) { return Math.ceil(fontSize * this.defaultLineHeight); } // Determines vertical offset modifier based on text baseline. static getVerticalModifier(textBaseline) { switch (textBaseline) { case "hanging": case "top": return 0; case "middle": return 0.5; case "alphabetic": case "bottom": case "ideographic": default: return 1; } } }; TextUtils.EllipsisChar = "\u2026"; // Representation for text clipping. TextUtils.defaultLineHeight = 1.15; // Normally between 1.1 and 1.2 TextUtils.lineSplitter = /\r?\n/g; var SimpleTextMeasurer = class { constructor(measureTextFn, textBaseline = "alphabetic") { this.measureTextFn = measureTextFn; this.textBaseline = textBaseline; // local chars width cache per TextMeasurer this.charMap = /* @__PURE__ */ new Map(); } // Measures metrics for a single line of text. getMetrics(text) { const m = this.measureTextFn(text); m.fontBoundingBoxAscent ?? (m.fontBoundingBoxAscent = m.emHeightAscent); m.fontBoundingBoxDescent ?? (m.fontBoundingBoxDescent = m.emHeightDescent); return { width: m.width, height: m.actualBoundingBoxAscent + m.actualBoundingBoxDescent, lineHeight: m.fontBoundingBoxAscent + m.fontBoundingBoxDescent, offsetTop: m.actualBoundingBoxAscent, offsetLeft: m.actualBoundingBoxLeft }; } // Calculates aggregated metrics for multiline text. getMultilineMetrics(lines) { let width = 0; let height = 0; let offsetTop = 0; let offsetLeft = 0; let baselineDistance = 0; const verticalModifier = TextUtils.getVerticalModifier(this.textBaseline); const lineMetrics = []; let index = 0; const length = lines.length; for (const line of lines) { const m = this.measureTextFn(line); m.fontBoundingBoxAscent ?? (m.fontBoundingBoxAscent = m.emHeightAscent); m.fontBoundingBoxDescent ?? (m.fontBoundingBoxDescent = m.emHeightDescent); if (width < m.width) { width = m.width; } if (offsetLeft < m.actualBoundingBoxLeft) { offsetLeft = m.actualBoundingBoxLeft; } if (index === 0) { height += m.actualBoundingBoxAscent; offsetTop += m.actualBoundingBoxAscent; } else { baselineDistance += m.fontBoundingBoxAscent; } if (index === length - 1) { height += m.actualBoundingBoxDescent; } else { baselineDistance += m.fontBoundingBoxDescent; } lineMetrics.push({ text: line, width: m.width, height: m.actualBoundingBoxAscent + m.actualBoundingBoxDescent, lineHeight: m.fontBoundingBoxAscent + m.fontBoundingBoxDescent, offsetTop: m.actualBoundingBoxAscent, offsetLeft: m.actualBoundingBoxLeft }); index++; } height += baselineDistance; offsetTop += baselineDistance * verticalModifier; return { width, height, offsetTop, offsetLeft, lineMetrics }; } textWidth(text, estimate) { if (estimate) { let estimatedWidth = 0; for (let i = 0; i < text.length; i++) { estimatedWidth += this.textWidth(text.charAt(i)); } return estimatedWidth; } if (text.length > 1) { return this.measureTextFn(text).width; } return this.charMap.get(text) ?? this.charWidth(text); } measureText(text) { return this.getMetrics(text); } // Measures the dimensions of the provided text, handling multiline if needed. measureLines(text) { const lines = typeof text === "string" ? text.split(TextUtils.lineSplitter) : text; return this.getMultilineMetrics(lines); } charWidth(char) { const { width } = this.measureTextFn(char); this.charMap.set(char, width); return width; } }; // packages/ag-charts-community/src/scene/matrix.ts var IDENTITY_MATRIX_ELEMENTS = [1, 0, 0, 1, 0, 0]; var Matrix = class _Matrix { get e() { return [...this.elements]; } constructor(elements = IDENTITY_MATRIX_ELEMENTS) { this.elements = [...elements]; } setElements(elements) { const e = this.elements; e[0] = elements[0]; e[1] = elements[1]; e[2] = elements[2]; e[3] = elements[3]; e[4] = elements[4]; e[5] = elements[5]; return this; } get identity() { const e = this.elements; return isNumberEqual(e[0], 1) && isNumberEqual(e[1], 0) && isNumberEqual(e[2], 0) && isNumberEqual(e[3], 1) && isNumberEqual(e[4], 0) && isNumberEqual(e[5], 0); } /** * Performs the AxB matrix multiplication and saves the result * to `C`, if given, or to `A` otherwise. */ AxB(A, B, C) { const a = A[0] * B[0] + A[2] * B[1], b = A[1] * B[0] + A[3] * B[1], c = A[0] * B[2] + A[2] * B[3], d = A[1] * B[2] + A[3] * B[3], e = A[0] * B[4] + A[2] * B[5] + A[4], f = A[1] * B[4] + A[3] * B[5] + A[5]; C = C ?? A; C[0] = a; C[1] = b; C[2] = c; C[3] = d; C[4] = e; C[5] = f; } /** * The `other` matrix gets post-multiplied to the current matrix. * Returns the current matrix. * @param other */ multiplySelf(other) { this.AxB(this.elements, other.elements); return this; } /** * The `other` matrix gets post-multiplied to the current matrix. * Returns a new matrix. * @param other */ multiply(other) { const elements = new Array(6); if (other instanceof _Matrix) { this.AxB(this.elements, other.elements, elements); } else { this.AxB(this.elements, [other.a, other.b, other.c, other.d, other.e, other.f], elements); } return new _Matrix(elements); } preMultiplySelf(other) { this.AxB(other.elements, this.elements, this.elements); return this; } /** * Returns the inverse of this matrix as a new matrix. */ inverse() { const el = this.elements; let a = el[0], b = el[1], c = el[2], d = el[3]; const e = el[4], f = el[5]; const rD = 1 / (a * d - b * c); a *= rD; b *= rD; c *= rD; d *= rD; return new _Matrix([d, -b, -c, a, c * f - d * e, b * e - a * f]); } invertSelf() { const el = this.elements; let a = el[0], b = el[1], c = el[2], d = el[3]; const e = el[4], f = el[5]; const rD = 1 / (a * d - b * c); a *= rD; b *= rD; c *= rD; d *= rD; el[0] = d; el[1] = -b; el[2] = -c; el[3] = a; el[4] = c * f - d * e; el[5] = b * e - a * f; return this; } transformPoint(x, y) { const e = this.elements; return { x: x * e[0] + y * e[2] + e[4], y: x * e[1] + y * e[3] + e[5] }; } transformBBox(bbox, target) { const el = this.elements; const xx = el[0]; const xy = el[1]; const yx = el[2]; const yy = el[3]; const h_w = bbox.width * 0.5; const h_h = bbox.height * 0.5; const cx = bbox.x + h_w; const cy = bbox.y + h_h; const w = Math.abs(h_w * xx) + Math.abs(h_h * yx); const h = Math.abs(h_w * xy) + Math.abs(h_h * yy); target ?? (target = new BBox(0, 0, 0, 0)); target.x = cx * xx + cy * yx + el[4] - w; target.y = cx * xy + cy * yy + el[5] - h; target.width = w + w; target.height = h + h; return target; } toContext(ctx) { if (this.identity) { return; } const e = this.elements; ctx.transform(e[0], e[1], e[2], e[3], e[4], e[5]); } static updateTransformMatrix(matrix, scalingX, scalingY, rotation, translationX, translationY, opts) { const sx = scalingX; const sy = scalingY; let scx; let scy; if (sx === 1 && sy === 1) { scx = 0; scy = 0; } else { scx = opts?.scalingCenterX ?? 0; scy = opts?.scalingCenterY ?? 0; } const r = rotation; const cos = Math.cos(r); const sin = Math.sin(r); let rcx; let rcy; if (r === 0) { rcx = 0; rcy = 0; } else { rcx = opts?.rotationCenterX ?? 0; rcy = opts?.rotationCenterY ?? 0; } const tx = translationX; const ty = translationY; const tx4 = scx * (1 - sx) - rcx; const ty4 = scy * (1 - sy) - rcy; matrix.setElements([ cos * sx, sin * sx, -sin * sy, cos * sy, cos * tx4 - sin * ty4 + rcx + tx, sin * tx4 + cos * ty4 + rcy + ty ]); return matrix; } }; // packages/ag-charts-community/src/scene/transformable.ts function isMatrixTransform(node) { return isMatrixTransformType(node.constructor); } var MATRIX_TRANSFORM_TYPE = Symbol("isMatrixTransform"); function isMatrixTransformType(cstr) { return cstr[MATRIX_TRANSFORM_TYPE] === true; } function MatrixTransform(Parent) { var _a, _b; const ParentNode = Parent; if (isMatrixTransformType(Parent)) { return Parent; } const TRANSFORM_MATRIX = Symbol("matrix_combined_transform"); class MatrixTransformInternal extends ParentNode { constructor() { super(...arguments); this[_b] = new Matrix(); this._dirtyTransform = true; } markDirtyTransform() { this._dirtyTransform = true; super.markDirty(); } updateMatrix(_matrix) { } computeTransformMatrix() { if (!this._dirtyTransform) return; this[TRANSFORM_MATRIX].setElements(IDENTITY_MATRIX_ELEMENTS); this.updateMatrix(this[TRANSFORM_MATRIX]); this._dirtyTransform = false; } toParent(bbox) { this.computeTransformMatrix(); if (this[TRANSFORM_MATRIX].identity) return bbox.clone(); return this[TRANSFORM_MATRIX].transformBBox(bbox); } toParentPoint(x, y) { this.computeTransformMatrix(); if (this[TRANSFORM_MATRIX].identity) return { x, y }; return this[TRANSFORM_MATRIX].transformPoint(x, y); } fromParent(bbox) { this.computeTransformMatrix(); if (this[TRANSFORM_MATRIX].identity) return bbox.clone(); return this[TRANSFORM_MATRIX].inverse().transformBBox(bbox); } fromParentPoint(x, y) { this.computeTransformMatrix(); if (this[TRANSFORM_MATRIX].identity) return { x, y }; return this[TRANSFORM_MATRIX].inverse().transformPoint(x, y); } computeBBox() { const bbox = super.computeBBox(); if (!bbox) return bbox; return this.toParent(bbox); } computeBBoxWithoutTransforms() { return super.computeBBox(); } pickNode(x, y, localCoords = false) { if (!localCoords) { ({ x, y } = this.fromParentPoint(x, y)); } return super.pickNode(x, y); } render(renderCtx) { this.computeTransformMatrix(); const { ctx } = renderCtx; const matrix = this[TRANSFORM_MATRIX]; let performRestore = false; if (!matrix.identity) { ctx.save(); performRestore = true; matrix.toContext(ctx); } super.render(renderCtx); if (performRestore) { ctx.restore(); } } toSVG() { this.computeTransformMatrix(); const svg = super.toSVG(); const matrix = this[TRANSFORM_MATRIX]; if (matrix.identity || svg == null) return svg; const g = createSvgElement("g"); g.append(...svg.elements); const [a, b, c, d, e, f] = matrix.e; g.setAttribute("transform", `matrix(${a} ${b} ${c} ${d} ${e} ${f})`); return { elements: [g], defs: svg.defs }; } } _a = MATRIX_TRANSFORM_TYPE, _b = TRANSFORM_MATRIX; MatrixTransformInternal[_a] = true; return MatrixTransformInternal; } function Rotatable(Parent) { var _a; const ParentNode = Parent; const ROTATABLE_MATRIX = Symbol("matrix_rotation"); class RotatableInternal extends MatrixTransform(ParentNode) { constructor() { super(...arguments); this[_a] = new Matrix(); this.rotationCenterX = null; this.rotationCenterY = null; this.rotation = 0; } updateMatrix(matrix) { super.updateMatrix(matrix); const { rotation, rotationCenterX, rotationCenterY } = this; if (rotation === 0) return; Matrix.updateTransformMatrix(this[ROTATABLE_MATRIX], 1, 1, rotation, 0, 0, { rotationCenterX, rotationCenterY }); matrix.multiplySelf(this[ROTATABLE_MATRIX]); } } _a = ROTATABLE_MATRIX; __decorateClass([ SceneChangeDetection({ type: "transform" }) ], RotatableInternal.prototype, "rotationCenterX", 2); __decorateClass([ SceneChangeDetection({ type: "transform" }) ], RotatableInternal.prototype, "rotationCenterY", 2); __decorateClass([ SceneChangeDetection({ type: "transform" }) ], RotatableInternal.prototype, "rotation", 2); return RotatableInternal; } function Scalable(Parent) { var _a; const ParentNode = Parent; const SCALABLE_MATRIX = Symbol("matrix_scale"); class ScalableInternal extends MatrixTransform(ParentNode) { constructor() { super(...arguments); this[_a] = new Matrix(); this.scalingX = 1; this.scalingY = 1; this.scalingCenterX = null; this.scalingCenterY = null; } updateMatrix(matrix) { super.updateMatrix(matrix); const { scalingX, scalingY, scalingCenterX, scalingCenterY } = this; if (scalingX === 1 && scalingY === 1) return; Matrix.updateTransformMatrix(this[SCALABLE_MATRIX], scalingX, scalingY, 0, 0, 0, { scalingCenterX, scalingCenterY }); matrix.multiplySelf(this[SCALABLE_MATRIX]); } } _a = SCALABLE_MATRIX; __decorateClass([ SceneChangeDetection({ type: "transform" }) ], ScalableInternal.prototype, "scalingX", 2); __decorateClass([ SceneChangeDetection({ type: "transform" }) ], ScalableInternal.prototype, "scalingY", 2); __decorateClass([ SceneChangeDetection({ type: "transform" }) ], ScalableInternal.prototype, "scalingCenterX", 2); __decorateClass([ SceneChangeDetection({ type: "transform" }) ], ScalableInternal.prototype, "scalingCenterY", 2); return ScalableInternal; } function Translatable(Parent) { var _a; const ParentNode = Parent; const TRANSLATABLE_MATRIX = Symbol("matrix_translation"); class TranslatableInternal extends MatrixTransform(ParentNode) { constructor() { super(...arguments); this[_a] = new Matrix(); this.translationX = 0; this.translationY = 0; } updateMatrix(matrix) { super.updateMatrix(matrix); const { translationX, translationY } = this; if (translationX === 0 && translationY === 0) return; Matrix.updateTransformMatrix(this[TRANSLATABLE_MATRIX], 1, 1, 0, translationX, translationY); matrix.multiplySelf(this[TRANSLATABLE_MATRIX]); } } _a = TRANSLATABLE_MATRIX; __decorateClass([ SceneChangeDetection({ type: "transform" }) ], TranslatableInternal.prototype, "translationX", 2); __decorateClass([ SceneChangeDetection({ type: "transform" }) ], TranslatableInternal.prototype, "translationY", 2); return TranslatableInternal; } var Transformable = class { /** * Converts a BBox from canvas coordinate space into the coordinate space of the given Node. */ static fromCanvas(node, bbox) { const parents = []; for (const parent of node.traverseUp()) { if (isMatrixTransform(parent)) { parents.unshift(parent); } } for (const parent of parents) { bbox = parent.fromParent(bbox); } if (isMatrixTransform(node)) { bbox = node.fromParent(bbox); } return bbox; } /** * Converts a Nodes BBox (or an arbitrary BBox if supplied) from local Node coordinate space * into the Canvas coordinate space. */ static toCanvas(node, bbox) { if (bbox == null) { bbox = node.getBBox(); } else if (isMatrixTransform(node)) { bbox = node.toParent(bbox); } for (const parent of node.traverseUp()) { if (isMatrixTransform(parent)) { bbox = parent.toParent(bbox); } } return bbox; } /** * Converts a point from canvas coordinate space into the coordinate space of the given Node. */ static fromCanvasPoint(node, x, y) { const parents = []; for (const parent of node.traverseUp()) { if (isMatrixTransform(parent)) { parents.unshift(parent); } } for (const parent of parents) { ({ x, y } = parent.fromParentPoint(x, y)); } if (isMatrixTransform(node)) { ({ x, y } = node.fromParentPoint(x, y)); } return { x, y }; } /** * Converts a point from a Nodes local coordinate space into the Canvas coordinate space. */ static toCanvasPoint(node, x, y) { if (isMatrixTransform(node)) { ({ x, y } = node.toParentPoint(x, y)); } for (const parent of node.traverseUp()) { if (isMatrixTransform(parent)) { ({ x, y } = parent.toParentPoint(x, y)); } } return { x, y }; } }; // packages/ag-charts-community/src/util/color.ts var lerp = (x, y, t) => x * (1 - t) + y * t; var srgbToLinear = (value) => { const sign = value < 0 ? -1 : 1; const abs = Math.abs(value); if (abs <= 0.04045) return value / 12.92; return sign * ((abs + 0.055) / 1.055) ** 2.4; }; var srgbFromLinear = (value) => { const sign = value < 0 ? -1 : 1; const abs = Math.abs(value); if (abs > 31308e-7) { return sign * (1.055 * abs ** (1 / 2.4) - 0.055); } return 12.92 * value; }; var _Color = class _Color { /** * Every color component should be in the [0, 1] range. * Some easing functions (such as elastic easing) can overshoot the target value by some amount. * So, when animating colors, if the source or target color components are already near * or at the edge of the allowed [0, 1] range, it is possible for the intermediate color * component value to end up outside of that range mid-animation. For this reason the constructor * performs range checking/constraining. * @param r Red component. * @param g Green component. * @param b Blue component. * @param a Alpha (opacity) component. */ constructor(r, g, b, a = 1) { this.r = clamp(0, r || 0, 1); this.g = clamp(0, g || 0, 1); this.b = clamp(0, b || 0, 1); this.a = clamp(0, a || 0, 1); } /** * A color string can be in one of the following formats to be valid: * - #rgb * - #rrggbb * - rgb(r, g, b) * - rgba(r, g, b, a) * - CSS color name such as 'white', 'orange', 'cyan', etc. */ static validColorString(str) { if (str.indexOf("#") >= 0) { return !!_Color.parseHex(str); } if (str.indexOf("rgb") >= 0) { return !!_Color.stringToRgba(str); } return _Color.nameToHex.has(str.toLowerCase()); } /** * The given string can be in one of the following formats: * - #rgb * - #rrggbb * - rgb(r, g, b) * - rgba(r, g, b, a) * - CSS color name such as 'white', 'orange', 'cyan', etc. * @param str */ static fromString(str) { if (str.indexOf("#") >= 0) { return _Color.fromHexString(str); } const hex = _Color.nameToHex.get(str.toLowerCase()); if (hex) { return _Color.fromHexString(hex); } if (str.indexOf("rgb") >= 0) { return _Color.fromRgbaString(str); } throw new Error(`Invalid color string: '${str}'`); } // See https://drafts.csswg.org/css-color/#hex-notation static parseHex(input) { input = input.replace(/ /g, "").slice(1); let parts; switch (input.length) { case 6: case 8: parts = []; for (let i = 0; i < input.length; i += 2) { parts.push(parseInt(`${input[i]}${input[i + 1]}`, 16)); } break; case 3: case 4: parts = input.split("").map((p) => parseInt(p, 16)).map((p) => p + p * 16); break; } if (parts?.length >= 3 && parts.every((p) => p >= 0)) { if (parts.length === 3) { parts.push(255); } return parts; } } static fromHexString(str) { const values = _Color.parseHex(str); if (values) { const [r, g, b, a] = values; return new _Color(r / 255, g / 255, b / 255, a / 255); } throw new Error(`Malformed hexadecimal color string: '${str}'`); } static stringToRgba(str) { let po = -1; let pc = -1; for (let i = 0; i < str.length; i++) { const c = str[i]; if (po === -1 && c === "(") { po = i; } else if (c === ")") { pc = i; break; } } if (po === -1 || pc === -1) return; const contents = str.substring(po + 1, pc); const parts = contents.split(","); const rgba = []; for (let i = 0; i < parts.length; i++) { const part = parts[i]; let value = parseFloat(part); if (!Number.isFinite(value)) { return; } if (part.indexOf("%") >= 0) { value = clamp(0, value, 100); value /= 100; } else if (i === 3) { value = clamp(0, value, 1); } else { value = clamp(0, value, 255); value /= 255; } rgba.push(value); } return rgba; } static fromRgbaString(str) { const rgba = _Color.stringToRgba(str); if (rgba) { if (rgba.length === 3) { return new _Color(rgba[0], rgba[1], rgba[2]); } else if (rgba.length === 4) { return new _Color(rgba[0], rgba[1], rgba[2], rgba[3]); } } throw new Error(`Malformed rgb/rgba color string: '${str}'`); } static fromArray(arr) { if (arr.length === 4) { return new _Color(arr[0], arr[1], arr[2], arr[3]); } if (arr.length === 3) { return new _Color(arr[0], arr[1], arr[2]); } throw new Error("The given array should contain 3 or 4 color components (numbers)."); } static fromHSB(h, s, b, alpha = 1) { const rgb = _Color.HSBtoRGB(h, s, b); return new _Color(rgb[0], rgb[1], rgb[2], alpha); } static fromHSL(h, s, l, alpha = 1) { const rgb = _Color.HSLtoRGB(h, s, l); return new _Color(rgb[0], rgb[1], rgb[2], alpha); } static fromOKLCH(l, c, h, alpha = 1) { const rgb = _Color.OKLCHtoRGB(l, c, h); return new _Color(rgb[0], rgb[1], rgb[2], alpha); } static padHex(str) { return str.length === 1 ? "0" + str : str; } toHexString() { let hex = "#" + _Color.padHex(Math.round(this.r * 255).toString(16)) + _Color.padHex(Math.round(this.g * 255).toString(16)) + _Color.padHex(Math.round(this.b * 255).toString(16)); if (this.a < 1) { hex += _Color.padHex(Math.round(this.a * 255).toString(16)); } return hex; } toRgbaString(fractionDigits = 3) { const components = [Math.round(this.r * 255), Math.round(this.g * 255), Math.round(this.b * 255)]; const k = Math.pow(10, fractionDigits); if (this.a !== 1) { components.push(Math.round(this.a * k) / k); return `rgba(${components.join(", ")})`; } return `rgb(${components.join(", ")})`; } toString() { if (this.a === 1) { return this.toHexString(); } return this.toRgbaString(); } toHSB() { return _Color.RGBtoHSB(this.r, this.g, this.b); } static RGBtoOKLCH(r, g, b) { const LSRGB0 = srgbToLinear(r); const LSRGB1 = srgbToLinear(g); const LSRGB2 = srgbToLinear(b); const LMS0 = Math.cbrt(0.4122214708 * LSRGB0 + 0.5363325363 * LSRGB1 + 0.0514459929 * LSRGB2); const LMS1 = Math.cbrt(0.2119034982 * LSRGB0 + 0.6806995451 * LSRGB1 + 0.1073969566 * LSRGB2); const LMS2 = Math.cbrt(0.0883024619 * LSRGB0 + 0.2817188376 * LSRGB1 + 0.6299787005 * LSRGB2); const OKLAB0 = 0.2104542553 * LMS0 + 0.793617785 * LMS1 - 0.0040720468 * LMS2; const OKLAB1 = 1.9779984951 * LMS0 - 2.428592205 * LMS1 + 0.4505937099 * LMS2; const OKLAB2 = 0.0259040371 * LMS0 + 0.7827717662 * LMS1 - 0.808675766 * LMS2; const hue = Math.atan2(OKLAB2, OKLAB1) * 180 / Math.PI; const OKLCH0 = OKLAB0; const OKLCH1 = Math.hypot(OKLAB1, OKLAB2); const OKLCH2 = hue >= 0 ? hue : hue + 360; return [OKLCH0, OKLCH1, OKLCH2]; } static OKLCHtoRGB(l, c, h) { const OKLAB0 = l; const OKLAB1 = c * Math.cos(h * Math.PI / 180); const OKLAB2 = c * Math.sin(h * Math.PI / 180); const LMS0 = (OKLAB0 + 0.3963377774 * OKLAB1 + 0.2158037573 * OKLAB2) ** 3; const LMS1 = (OKLAB0 - 0.1055613458 * OKLAB1 - 0.0638541728 * OKLAB2) ** 3; const LMS2 = (OKLAB0 - 0.0894841775 * OKLAB1 - 1.291485548 * OKLAB2) ** 3; const LSRGB0 = 4.0767416621 * LMS0 - 3.3077115913 * LMS1 + 0.2309699292 * LMS2; const LSRGB1 = -1.2684380046 * LMS0 + 2.6097574011 * LMS1 - 0.3413193965 * LMS2; const LSRGB2 = -0.0041960863 * LMS0 - 0.7034186147 * LMS1 + 1.707614701 * LMS2; const SRGB0 = srgbFromLinear(LSRGB0); const SRGB1 = srgbFromLinear(LSRGB1); const SRGB2 = srgbFromLinear(LSRGB2); return [SRGB0, SRGB1, SRGB2]; } static RGBtoHSL(r, g, b) { const min = Math.min(r, g, b); const max = Math.max(r, g, b); const l = (max + min) / 2; let h; let s; if (max === min) { h = 0; s = 0; } else { const delta3 = max - min; s = l > 0.5 ? delta3 / (2 - max - min) : delta3 / (max + min); if (max === r) { h = (g - b) / delta3 + (g < b ? 6 : 0); } else if (max === g) { h = (b - r) / delta3 + 2; } else { h = (r - g) / delta3 + 4; } h *= 360 / 6; } return [h, s, l]; } static HSLtoRGB(h, s, l) { h = (h % 360 + 360) % 360; if (s === 0) { return [l, l, l]; } const q = l < 0.5 ? l * (1 + s) : l + s - l * s; const p = 2 * l - q; function hueToRgb(t) { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; return p; } const r = hueToRgb(h / 360 + 1 / 3); const g = hueToRgb(h / 360); const b = hueToRgb(h / 360 - 1 / 3); return [r, g, b]; } /** * Converts the given RGB triple to an array of HSB (HSV) components. */ static RGBtoHSB(r, g, b) { const min = Math.min(r, g, b); const max = Math.max(r, g, b); const S = max === 0 ? 0 : (max - min) / max; let H = 0; if (min !== max) { const delta3 = max - min; const rc = (max - r) / delta3; const gc = (max - g) / delta3; const bc = (max - b) / delta3; if (r === max) { H = bc - gc; } else if (g === max) { H = 2 + rc - bc; } else { H = 4 + gc - rc; } H /= 6; if (H < 0) { H = H + 1; } } return [H * 360, S, max]; } /** * Converts the given HSB (HSV) triple to an array of RGB components. */ static HSBtoRGB(H, S, B) { H = (H % 360 + 360) % 360 / 360; let r = 0; let g = 0; let b = 0; if (S === 0) { r = g = b = B; } else { const h = (H - Math.floor(H)) * 6; const f = h - Math.floor(h); const p = B * (1 - S); const q = B * (1 - S * f); const t = B * (1 - S * (1 - f)); switch (h >> 0) { case 0: r = B; g = t; b = p; break; case 1: r = q; g = B; b = p; break; case 2: r = p; g = B; b = t; break; case 3: r = p; g = q; b = B; break; case 4: r = t; g = p; b = B; break; case 5: r = B; g = p; b = q; break; } } return [r, g, b]; } static mix(c0, c1, t) { return new _Color(lerp(c0.r, c1.r, t), lerp(c0.g, c1.g, t), lerp(c0.b, c1.b, t), lerp(c0.a, c1.a, t)); } }; /** * CSS Color Module Level 4: * https://drafts.csswg.org/css-color/#named-colors */ _Color.nameToHex = /* @__PURE__ */ new Map([ ["aliceblue", "#F0F8FF"], ["antiquewhite", "#FAEBD7"], ["aqua", "#00FFFF"], ["aquamarine", "#7FFFD4"], ["azure", "#F0FFFF"], ["beige", "#F5F5DC"], ["bisque", "#FFE4C4"], ["black", "#000000"], ["blanchedalmond", "#FFEBCD"], ["blue", "#0000FF"], ["blueviolet", "#8A2BE2"], ["brown", "#A52A2A"], ["burlywood", "#DEB887"], ["cadetblue", "#5F9EA0"], ["chartreuse", "#7FFF00"], ["chocolate", "#D2691E"], ["coral", "#FF7F50"], ["cornflowerblue", "#6495ED"], ["cornsilk", "#FFF8DC"], ["crimson", "#DC143C"], ["cyan", "#00FFFF"], ["darkblue", "#00008B"], ["darkcyan", "#008B8B"], ["darkgoldenrod", "#B8860B"], ["darkgray", "#A9A9A9"], ["darkgreen", "#006400"], ["darkgrey", "#A9A9A9"], ["darkkhaki", "#BDB76B"], ["darkmagenta", "#8B008B"], ["darkolivegreen", "#556B2F"], ["darkorange", "#FF8C00"], ["darkorchid", "#9932CC"], ["darkred", "#8B0000"], ["darksalmon", "#E9967A"], ["darkseagreen", "#8FBC8F"], ["darkslateblue", "#483D8B"], ["darkslategray", "#2F4F4F"], ["darkslategrey", "#2F4F4F"], ["darkturquoise", "#00CED1"], ["darkviolet", "#9400D3"], ["deeppink", "#FF1493"], ["deepskyblue", "#00BFFF"], ["dimgray", "#696969"], ["dimgrey", "#696969"], ["dodgerblue", "#1E90FF"], ["firebrick", "#B22222"], ["floralwhite", "#FFFAF0"], ["forestgreen", "#228B22"], ["fuchsia", "#FF00FF"], ["gainsboro", "#DCDCDC"], ["ghostwhite", "#F8F8FF"], ["gold", "#FFD700"], ["goldenrod", "#DAA520"], ["gray", "#808080"], ["green", "#008000"], ["greenyellow", "#ADFF2F"], ["grey", "#808080"], ["honeydew", "#F0FFF0"], ["hotpink", "#FF69B4"], ["indianred", "#CD5C5C"], ["indigo", "#4B0082"], ["ivory", "#FFFFF0"], ["khaki", "#F0E68C"], ["lavender", "#E6E6FA"], ["lavenderblush", "#FFF0F5"], ["lawngreen", "#7CFC00"], ["lemonchiffon", "#FFFACD"], ["lightblue", "#ADD8E6"], ["lightcoral", "#F08080"], ["lightcyan", "#E0FFFF"], ["lightgoldenrodyellow", "#FAFAD2"], ["lightgray", "#D3D3D3"], ["lightgreen", "#90EE90"], ["lightgrey", "#D3D3D3"], ["lightpink", "#FFB6C1"], ["lightsalmon", "#FFA07A"], ["lightseagreen", "#20B2AA"], ["lightskyblue", "#87CEFA"], ["lightslategray", "#778899"], ["lightslategrey", "#778899"], ["lightsteelblue", "#B0C4DE"], ["lightyellow", "#FFFFE0"], ["lime", "#00FF00"], ["limegreen", "#32CD32"], ["linen", "#FAF0E6"], ["magenta", "#FF00FF"], ["maroon", "#800000"], ["mediumaquamarine", "#66CDAA"], ["mediumblue", "#0000CD"], ["mediumorchid", "#BA55D3"], ["mediumpurple", "#9370DB"], ["mediumseagreen", "#3CB371"], ["mediumslateblue", "#7B68EE"], ["mediumspringgreen", "#00FA9A"], ["mediumturquoise", "#48D1CC"], ["mediumvioletred", "#C71585"], ["midnightblue", "#191970"], ["mintcream", "#F5FFFA"], ["mistyrose", "#FFE4E1"], ["moccasin", "#FFE4B5"], ["navajowhite", "#FFDEAD"], ["navy", "#000080"], ["oldlace", "#FDF5E6"], ["olive", "#808000"], ["olivedrab", "#6B8E23"], ["orange", "#FFA500"], ["orangered", "#FF4500"], ["orchid", "#DA70D6"], ["palegoldenrod", "#EEE8AA"], ["palegreen", "#98FB98"], ["paleturquoise", "#AFEEEE"], ["palevioletred", "#DB7093"], ["papayawhip", "#FFEFD5"], ["peachpuff", "#FFDAB9"], ["peru", "#CD853F"], ["pink", "#FFC0CB"], ["plum", "#DDA0DD"], ["powderblue", "#B0E0E6"], ["purple", "#800080"], ["rebeccapurple", "#663399"], ["red", "#FF0000"], ["rosybrown", "#BC8F8F"], ["royalblue", "#4169E1"], ["saddlebrown", "#8B4513"], ["salmon", "#FA8072"], ["sandybrown", "#F4A460"], ["seagreen", "#2E8B57"], ["seashell", "#FFF5EE"], ["sienna", "#A0522D"], ["silver", "#C0C0C0"], ["skyblue", "#87CEEB"], ["slateblue", "#6A5ACD"], ["slategray", "#708090"], ["slategrey", "#708090"], ["snow", "#FFFAFA"], ["springgreen", "#00FF7F"], ["steelblue", "#4682B4"], ["tan", "#D2B48C"], ["teal", "#008080"], ["thistle", "#D8BFD8"], ["tomato", "#FF6347"], ["transparent", "#00000000"], ["turquoise", "#40E0D0"], ["violet", "#EE82EE"], ["wheat", "#F5DEB3"], ["white", "#FFFFFF"], ["whitesmoke", "#F5F5F5"], ["yellow", "#FFFF00"], ["yellowgreen", "#9ACD32"] ]); var Color = _Color; // packages/ag-charts-community/src/scale/abstractScale.ts var AbstractScale = class { ticks(_ticks, _domain, _visibleRange) { return void 0; } niceDomain(_ticks, domain = this.domain) { return domain; } tickFormatter(_params) { return void 0; } datumFormatter(_params) { return void 0; } get bandwidth() { return void 0; } get step() { return void 0; } get inset() { return void 0; } }; // packages/ag-charts-community/src/scale/invalidating.ts var Invalidating = (target, propertyKey) => { const mappedProperty = Symbol(String(propertyKey)); target[mappedProperty] = void 0; Object.defineProperty(target, propertyKey, { get() { return this[mappedProperty]; }, set(newValue) { const oldValue = this[mappedProperty]; if (oldValue !== newValue) { this[mappedProperty] = newValue; this.invalid = true; } }, enumerable: true, configurable: false }); }; // packages/ag-charts-community/src/scale/colorScale.ts var convertColorStringToOklcha = (v) => { const color = Color.fromString(v); const [l, c, h] = Color.RGBtoOKLCH(color.r, color.g, color.b); return { l, c, h, a: color.a }; }; var delta = 1e-6; var isAchromatic = (x) => x.c < delta || x.l < delta || x.l > 1 - delta; var interpolateOklch = (x, y, d) => { d = clamp(0, d, 1); let h; if (isAchromatic(x)) { h = y.h; } else if (isAchromatic(y)) { h = x.h; } else { const xH = x.h; let yH = y.h; const deltaH = y.h - x.h; if (deltaH > 180) { yH -= 360; } else if (deltaH < -180) { yH += 360; } h = xH * (1 - d) + yH * d; } const c = x.c * (1 - d) + y.c * d; const l = x.l * (1 - d) + y.l * d; const a = x.a * (1 - d) + y.a * d; return Color.fromOKLCH(l, c, h, a); }; var ColorScale = class extends AbstractScale { constructor() { super(...arguments); this.type = "color"; this.invalid = true; this.domain = [0, 1]; this.range = ["red", "blue"]; this.parsedRange = this.range.map(convertColorStringToOklcha); } update() { const { domain, range: range2 } = this; if (domain.length < 2) { logger_exports.warnOnce("`colorDomain` should have at least 2 values."); if (domain.length === 0) { domain.push(0, 1); } else if (domain.length === 1) { domain.push(domain[0] + 1); } } for (let i = 1; i < domain.length; i++) { const a = domain[i - 1]; const b = domain[i]; if (a >= b) { logger_exports.warnOnce("`colorDomain` values should be supplied in ascending order."); domain.sort((a2, b2) => a2 - b2); break; } } if (range2.length < domain.length) { for (let i = range2.length; i < domain.length; i++) { range2.push(range2.length > 0 ? range2[0] : "black"); } } this.parsedRange = this.range.map(convertColorStringToOklcha); } normalizeDomains(...domains) { return { domain: domains.flat(), animatable: true }; } toDomain() { return; } convert(x) { this.refresh(); const { domain, range: range2, parsedRange } = this; const d0 = domain[0]; const d1 = domain.at(-1); const r0 = range2[0]; const r1 = range2[range2.length - 1]; if (x <= d0) { return r0; } if (x >= d1) { return r1; } let index; let q; if (domain.length === 2) { const t = (x - d0) / (d1 - d0); const step = 1 / (range2.length - 1); index = range2.length <= 2 ? 0 : Math.min(Math.floor(t * (range2.length - 1)), range2.length - 2); q = (t - index * step) / step; } else { for (index = 0; index < domain.length - 2; index++) { if (x < domain[index + 1]) { break; } } const a = domain[index]; const b = domain[index + 1]; q = (x - a) / (b - a); } const c0 = parsedRange[index]; const c1 = parsedRange[index + 1]; return interpolateOklch(c0, c1, q).toRgbaString(); } invert() { return; } refresh() { if (!this.invalid) return; this.invalid = false; this.update(); if (this.invalid) { logger_exports.warnOnce("Expected update to not invalidate scale"); } } }; __decorateClass([ Invalidating ], ColorScale.prototype, "domain", 2); __decorateClass([ Invalidating ], ColorScale.prototype, "range", 2); // packages/ag-charts-community/src/scene/gradient/gradient.ts var Gradient = class { constructor(colorSpace, stops = [], bbox) { this.colorSpace = colorSpace; this.stops = stops; this.bbox = bbox; this._cache = void 0; } createGradient(ctx, shapeBbox) { const bbox = this.bbox ?? shapeBbox; if (this._cache != null && this._cache.ctx === ctx && this._cache.bbox.equals(bbox)) { return this._cache.gradient; } const { stops, colorSpace } = this; if (stops.length === 0) return; if (stops.length === 1) return stops[0].color; let gradient = this.createCanvasGradient(ctx, bbox); if (gradient == null) return; const isOkLch = colorSpace === "oklch"; const step = 0.05; let c0 = stops[0]; gradient.addColorStop(c0.offset, c0.color); for (let i = 1; i < stops.length; i += 1) { const c1 = stops[i]; if (isOkLch) { const scale = new ColorScale(); scale.domain = [c0.offset, c1.offset]; scale.range = [c0.color, c1.color]; for (let offset4 = c0.offset + step; offset4 < c1.offset; offset4 += step) { gradient.addColorStop(offset4, scale.convert(offset4)); } } gradient.addColorStop(c1.offset, c1.color); c0 = c1; } if ("createPattern" in gradient) { gradient = gradient.createPattern(); } this._cache = { ctx, bbox, gradient }; return gradient; } }; // packages/ag-charts-community/src/util/angle.ts var twoPi = Math.PI * 2; var halfPi = Math.PI / 2; function normalizeAngle360(radians) { radians %= twoPi; radians += twoPi; radians %= twoPi; return radians; } function normalizeAngle180(radians) { radians %= twoPi; if (radians < -Math.PI) { radians += twoPi; } else if (radians >= Math.PI) { radians -= twoPi; } return radians; } function isBetweenAngles(targetAngle, startAngle, endAngle) { const t = normalizeAngle360(targetAngle); const a0 = normalizeAngle360(startAngle); const a1 = normalizeAngle360(endAngle); if (a0 < a1) { return a0 <= t && t <= a1; } else if (a0 > a1) { return a0 <= t || t <= a1; } else { return true; } } function toRadians(degrees) { return degrees / 180 * Math.PI; } function angleBetween(angle0, angle1) { angle0 = normalizeAngle360(angle0); angle1 = normalizeAngle360(angle1); return angle1 - angle0 + (angle0 > angle1 ? twoPi : 0); } // packages/ag-charts-community/src/scene/gradient/linearGradient.ts var LinearGradient = class extends Gradient { constructor(colorSpace, stops, angle = 0, bbox) { super(colorSpace, stops, bbox); this.angle = angle; } createCanvasGradient(ctx, bbox) { const angleOffset = 90; const { angle } = this; const radians = normalizeAngle360(toRadians(angle + angleOffset)); const cos = Math.cos(radians); const sin = Math.sin(radians); const w = bbox.width; const h = bbox.height; const cx = bbox.x + w * 0.5; const cy = bbox.y + h * 0.5; const diagonal = Math.sqrt(h * h + w * w) / 2; const diagonalAngle = Math.atan2(h, w); let quarteredAngle; if (radians < Math.PI / 2) { quarteredAngle = radians; } else if (radians < Math.PI) { quarteredAngle = Math.PI - radians; } else if (radians < 1.5 * Math.PI) { quarteredAngle = radians - Math.PI; } else { quarteredAngle = 2 * Math.PI - radians; } const l = diagonal * Math.abs(Math.cos(quarteredAngle - diagonalAngle)); return ctx.createLinearGradient(cx + cos * l, cy + sin * l, cx - cos * l, cy - sin * l); } }; // packages/ag-charts-community/src/util/decorator.ts var BREAK_TRANSFORM_CHAIN = Symbol("BREAK"); var CONFIG_KEY = "__decorator_config"; function initialiseConfig(target, propertyKeyOrSymbol) { if (Object.getOwnPropertyDescriptor(target, CONFIG_KEY) == null) { Object.defineProperty(target, CONFIG_KEY, { value: {} }); } const config = target[CONFIG_KEY]; const propertyKey = propertyKeyOrSymbol.toString(); if (typeof config[propertyKey] !== "undefined") { return config[propertyKey]; } const valuesMap = /* @__PURE__ */ new WeakMap(); config[propertyKey] = { setters: [], getters: [], observers: [], valuesMap }; const descriptor = Object.getOwnPropertyDescriptor(target, propertyKeyOrSymbol); const prevSet = descriptor?.set; const prevGet = descriptor?.get; const getter = function() { let value = prevGet ? prevGet.call(this) : valuesMap.get(this); for (const transformFn of config[propertyKey].getters) { value = transformFn(this, propertyKeyOrSymbol, value); if (value === BREAK_TRANSFORM_CHAIN) { return; } } return value; }; const setter = function(value) { const { setters, observers } = config[propertyKey]; let oldValue; if (setters.some((f) => f.length > 2)) { oldValue = prevGet ? prevGet.call(this) : valuesMap.get(this); } for (const transformFn of setters) { value = transformFn(this, propertyKeyOrSymbol, value, oldValue); if (value === BREAK_TRANSFORM_CHAIN) { return; } } if (prevSet) { prevSet.call(this, value); } else { valuesMap.set(this, value); } for (const observerFn of observers) { observerFn(this, value, oldValue); } }; Object.defineProperty(target, propertyKeyOrSymbol, { set: setter, get: getter, enumerable: true, configurable: false }); return config[propertyKey]; } function addTransformToInstanceProperty(setTransform, getTransform, configMetadata) { return (target, propertyKeyOrSymbol) => { const config = initialiseConfig(target, propertyKeyOrSymbol); config.setters.push(setTransform); if (getTransform) { config.getters.unshift(getTransform); } if (configMetadata) { Object.assign(config, configMetadata); } }; } function addObserverToInstanceProperty(setObserver) { return (target, propertyKeyOrSymbol) => { initialiseConfig(target, propertyKeyOrSymbol).observers.push(setObserver); }; } function isDecoratedObject(target) { return typeof target !== "undefined" && CONFIG_KEY in target; } function listDecoratedProperties(target) { const targets = /* @__PURE__ */ new Set(); while (isDecoratedObject(target)) { targets.add(target?.[CONFIG_KEY]); target = Object.getPrototypeOf(target); } return Array.from(targets).flatMap((configMap) => Object.keys(configMap)); } function extractDecoratedPropertyMetadata(target, propertyKeyOrSymbol) { const propertyKey = propertyKeyOrSymbol.toString(); while (isDecoratedObject(target)) { const config = target[CONFIG_KEY]; if (Object.hasOwn(config, propertyKey)) { return config[propertyKey]; } target = Object.getPrototypeOf(target); } } // packages/ag-charts-community/src/util/properties.ts var BaseProperties = class { set(properties) { const { className = this.constructor.name } = this.constructor; if (typeof properties !== "object") { logger_exports.warn(`unable to set ${className} - expecting a properties object`); return this; } const keys = new Set(Object.keys(properties)); for (const propertyKey of listDecoratedProperties(this)) { if (keys.has(propertyKey)) { const value = properties[propertyKey]; const self = this; if (isProperties(self[propertyKey])) { if (self[propertyKey] instanceof PropertiesArray) { const array = self[propertyKey].reset(value); if (array != null) { self[propertyKey] = array; } else { logger_exports.warn(`unable to set [${propertyKey}] - expecting a properties array`); } } else { self[propertyKey].set(value); } } else { self[propertyKey] = value; } keys.delete(propertyKey); } } for (const unknownKey of keys) { logger_exports.warn(`unable to set [${unknownKey}] in ${className} - property is unknown`); } return this; } isValid(warningPrefix) { return listDecoratedProperties(this).every((propertyKey) => { const { optional } = extractDecoratedPropertyMetadata(this, propertyKey); const valid = optional === true || typeof this[propertyKey] !== "undefined"; if (!valid) { logger_exports.warnOnce(`${warningPrefix ?? ""}[${propertyKey}] is required.`); } return valid; }); } toJson() { return listDecoratedProperties(this).reduce((object, propertyKey) => { const propertyValue = this[propertyKey]; object[propertyKey] = isProperties(propertyValue) ? propertyValue.toJson() : propertyValue; return object; }, {}); } }; var PropertiesArray = class _PropertiesArray extends Array { constructor(itemFactory, ...properties) { super(properties.length); const isConstructor = (value2) => Boolean(value2?.prototype?.constructor?.name); const value = isConstructor(itemFactory) ? (params) => new itemFactory().set(params) : itemFactory; Object.defineProperty(this, "itemFactory", { value, enumerable: false, configurable: false }); this.set(properties); } set(properties) { if (isArray(properties)) { this.length = properties.length; for (let i = 0; i < properties.length; i++) { this[i] = this.itemFactory(properties[i]); } } return this; } reset(properties) { if (Array.isArray(properties)) { return new _PropertiesArray(this.itemFactory, ...properties); } } toJson() { return this.map((value) => value?.toJson?.() ?? value); } }; function isProperties(value) { return value instanceof BaseProperties || value instanceof PropertiesArray; } // packages/ag-charts-community/src/util/validation.ts function Validate(predicate, options = {}) { const { optional = false, property: overrideProperty } = options; return addTransformToInstanceProperty( (target, property, value) => { const context = { ...options, target, property }; if (optional && typeof value === "undefined" || predicate(value, context)) { if (isProperties(target[property]) && !isProperties(value)) { target[property].set(value); return target[property]; } return value; } const cleanKey = overrideProperty ?? String(property).replace(/^_*/, ""); const targetName = target.constructor.className ?? target.constructor.name.replace(/Properties$/, ""); const valueString = stringifyValue(value, 50); logger_exports.warn( `Property [${cleanKey}] of [${targetName}] cannot be set to [${valueString}]${predicate.message ? `; expecting ${getPredicateMessage(predicate, context)}` : ""}, ignoring.` ); return BREAK_TRANSFORM_CHAIN; }, void 0, { optional } ); } var AND = (...predicates) => { const messages = []; return predicateWithMessage( (value, ctx) => { messages.length = 0; return predicates.every((predicate) => { const isValid = predicate(value, ctx); if (!isValid) { messages.push(getPredicateMessage(predicate, ctx)); } return isValid; }); }, () => messages.filter(Boolean).join(" AND ") ); }; var OR = (...predicates) => predicateWithMessage( (value, ctx) => predicates.some((predicate) => predicate(value, ctx)), (ctx) => predicates.map(getPredicateMessageMapper(ctx)).filter(Boolean).join(" OR ") ); var OBJECT = attachObjectRestrictions( predicateWithMessage( (value, ctx) => isProperties(value) || isObject(value) && isProperties(ctx.target[ctx.property]), "a properties object" ) ); var PLAIN_OBJECT = attachObjectRestrictions(predicateWithMessage((value) => isObject(value), "an object")); var BOOLEAN = predicateWithMessage(isBoolean, "a boolean"); var FUNCTION = predicateWithMessage(isFunction, "a function"); var STRING = predicateWithMessage(isString, "a string"); var NUMBER = attachNumberRestrictions(predicateWithMessage(isFiniteNumber, "a number")); var REAL_NUMBER = predicateWithMessage((value) => isNumber(value) && !isNaN(value), "a real number"); var NAN = predicateWithMessage((value) => isNumber(value) && isNaN(value), "NaN"); var POSITIVE_NUMBER = NUMBER.restrict({ min: 0 }); var RATIO = NUMBER.restrict({ min: 0, max: 1 }); var NUMBER_OR_NAN = OR(NUMBER, NAN); var ARRAY = attachArrayRestrictions(predicateWithMessage(isArray, "an array")); var ARRAY_OF = (predicate, message) => predicateWithMessage( (value, ctx) => isArray(value) && value.every((item) => predicate(item, ctx)), (ctx) => { const arrayMessage = getPredicateMessage(ARRAY, ctx) ?? ""; if (typeof message === "function") { return `${arrayMessage} of ${message(ctx)}`; } return message ? `${arrayMessage} of ${message}` : arrayMessage; } ); var isComparable = (value) => isFiniteNumber(value) || isValidDate(value); var LESS_THAN = (otherField) => predicateWithMessage( (v, ctx) => !isComparable(v) || !isComparable(ctx.target[otherField]) || v < ctx.target[otherField], `to be less than ${otherField}` ); var GREATER_THAN = (otherField) => predicateWithMessage( (v, ctx) => !isComparable(v) || !isComparable(ctx.target[otherField]) || v > ctx.target[otherField], `to be greater than ${otherField}` ); var DATE = predicateWithMessage(isValidDate, "Date object"); var DATE_OR_DATETIME_MS = OR(DATE, POSITIVE_NUMBER); var colorMessage = `A color string can be in one of the following formats to be valid: #rgb, #rrggbb, rgb(r, g, b), rgba(r, g, b, a) or a CSS color name such as 'white', 'orange', 'cyan', etc`; var COLOR_STRING = predicateWithMessage( (v) => isString(v) && Color.validColorString(v), `color String. ${colorMessage}` ); var COLOR_GRADIENT = attachObjectRestrictions( predicateWithMessage((value) => isObject(value) && value.type === "gradient", "a color gradient object") ); var COLOR_STRING_ARRAY = predicateWithMessage(ARRAY_OF(COLOR_STRING), `color strings. ${colorMessage}`); var BOOLEAN_ARRAY = ARRAY_OF(BOOLEAN, "boolean values"); var NUMBER_ARRAY = ARRAY_OF(NUMBER, "numbers"); var STRING_ARRAY = ARRAY_OF(STRING, "strings"); var DATE_ARRAY = predicateWithMessage(ARRAY_OF(DATE), "Date objects"); var OBJECT_ARRAY = predicateWithMessage(ARRAY_OF(OBJECT), "objects"); var LINE_CAP = UNION(["butt", "round", "square"], "a line cap"); var LINE_JOIN = UNION(["round", "bevel", "miter"], "a line join"); var LINE_STYLE = UNION(["solid", "dashed", "dotted"], "a line style"); var LINE_DASH = predicateWithMessage( ARRAY_OF(POSITIVE_NUMBER), "numbers specifying the length in pixels of alternating dashes and gaps, for example, [6, 3] means dashes with a length of 6 pixels with gaps between of 3 pixels." ); var POSITION = UNION(["top", "right", "bottom", "left"], "a position"); var FONT_STYLE = UNION(["normal", "italic", "oblique"], "a font style"); var FONT_WEIGHT = OR( UNION(["normal", "bold", "bolder", "lighter"], "a font weight"), NUMBER.restrict({ min: 1, max: 1e3 }) ); var TEXT_WRAP = UNION(["never", "always", "hyphenate", "on-space"], "a text wrap strategy"); var TEXT_ALIGN = UNION(["left", "center", "right"], "a text align"); var VERTICAL_ALIGN = UNION(["top", "middle", "bottom"], "a vertical align"); var OVERFLOW_STRATEGY = UNION(["ellipsis", "hide"], "an overflow strategy"); var DIRECTION = UNION(["horizontal", "vertical"], "a direction"); var PLACEMENT = UNION(["inside", "outside"], "a placement"); var INTERACTION_RANGE = OR(UNION(["exact", "nearest"], "interaction range"), NUMBER); var LABEL_PLACEMENT = UNION(["top", "bottom", "left", "right"]); function UNION(options, message = "a") { return predicateWithMessage( (v, ctx) => { const option = options.find((o) => { const value = typeof o === "string" ? o : o.value; return v === value; }); if (option == null) return false; if (typeof option !== "string" && (option.deprecated === true || option.deprecatedTo != null)) { const messages = [`Property [%s] with value '${option.value}' is deprecated.`]; if (option.deprecatedTo) { messages.push(`Use ${option.deprecatedTo} instead.`); } logger_exports.warnOnce(messages.join(" "), ctx.property); } return true; }, `${message} keyword such as ${joinUnionOptions(options)}` ); } var MIN_SPACING = OR(AND(NUMBER.restrict({ min: 1 }), LESS_THAN("maxSpacing")), NAN); var MAX_SPACING = OR(AND(NUMBER.restrict({ min: 1 }), GREATER_THAN("minSpacing")), NAN); function predicateWithMessage(predicate, message) { predicate.message = message; return predicate; } function joinUnionOptions(options) { const values = options.filter((option) => typeof option === "string" || option.undocumented !== true).map((option) => `'${typeof option === "string" ? option : option.value}'`); if (values.length === 1) { return values[0]; } const lastValue = values.pop(); return `${values.join(", ")} or ${lastValue}`; } function getPredicateMessage(predicate, ctx) { return isFunction(predicate.message) ? predicate.message(ctx) : predicate.message; } function getPredicateMessageMapper(ctx) { return (predicate) => getPredicateMessage(predicate, ctx); } function attachArrayRestrictions(predicate) { return Object.assign(predicate, { restrict({ length, minLength } = {}) { let message = "an array"; if (isNumber(minLength) && minLength > 0) { message = "a non-empty array"; } else if (isNumber(length)) { message = `an array of length ${length}`; } return predicateWithMessage( (value) => isArray(value) && (isNumber(length) ? value.length === length : true) && (isNumber(minLength) ? value.length >= minLength : true), message ); } }); } function attachNumberRestrictions(predicate) { return Object.assign(predicate, { restrict({ min, max } = {}) { const message = ["a number"]; const hasMin = isNumber(min); const hasMax = isNumber(max); if (hasMin && hasMax) { message.push(`between ${min} and ${max} inclusive`); } else if (hasMin) { message.push(`greater than or equal to ${min}`); } else if (hasMax) { message.push(`less than or equal to ${max}`); } return predicateWithMessage( (value) => isFiniteNumber(value) && (hasMin ? value >= min : true) && (hasMax ? value <= max : true), message.join(" ") ); } }); } function attachObjectRestrictions(predicate) { return Object.assign(predicate, { restrict(objectType) { return predicateWithMessage( (value) => value instanceof objectType, (ctx) => getPredicateMessage(predicate, ctx) ?? `an instance of ${objectType.name}` ); } }); } // packages/ag-charts-community/src/scene/gradient/stops.ts var StopProperties = class extends BaseProperties { constructor() { super(...arguments); this.color = "black"; } }; __decorateClass([ Validate(NUMBER, { optional: true }) ], StopProperties.prototype, "stop", 2); __decorateClass([ Validate(COLOR_STRING, { optional: true }) ], StopProperties.prototype, "color", 2); function stopsAreAscending(fills) { let currentStop; for (const { stop } of fills) { if (stop == null) { continue; } else if (currentStop != null && stop < currentStop) { return false; } currentStop = stop; } return true; } function discreteColorStops(colorStops) { return colorStops.flatMap((colorStop, i) => { const { offset: offset4 } = colorStop; const nextColor = colorStops.at(i + 1)?.color; return nextColor != null ? [colorStop, { offset: offset4, color: nextColor }] : [colorStop]; }); } function getDefaultColorStops(defaultColorStops, fillMode) { const stopOffset = fillMode === "discrete" ? 1 : 0; const colorStops = defaultColorStops.map( (color, index, { length }) => ({ offset: (index + stopOffset) / (length - 1 + stopOffset), color }) ); return fillMode === "discrete" ? discreteColorStops(colorStops) : colorStops; } function getColorStops(fills, defaultColorStops, domain, fillMode = "continuous") { if (fills.length === 0) { return getDefaultColorStops(defaultColorStops, fillMode); } else if (!stopsAreAscending(fills)) { logger_exports.warnOnce(`[fills] must have the stops defined in ascending order`); return []; } const d0 = Math.min(...domain); const d1 = Math.max(...domain); const isDiscrete = fillMode === "discrete"; const offsets = new Float64Array(fills.length); let previousDefinedStopIndex = 0; let nextDefinedStopIndex = -1; for (let i = 0; i < fills.length; i += 1) { const colorStop = fills[i]; if (i >= nextDefinedStopIndex) { nextDefinedStopIndex = fills.length - 1; for (let j = i + 1; j < fills.length; j += 1) { if (fills[j].stop != null) { nextDefinedStopIndex = j; break; } } } let { stop } = colorStop; if (stop == null) { const stop0 = fills[previousDefinedStopIndex].stop; const stop1 = fills[nextDefinedStopIndex].stop; const value0 = stop0 ?? d0; const value1 = stop1 ?? d1; const stopOffset = isDiscrete && stop0 == null ? 1 : 0; stop = value0 + (value1 - value0) * (i - previousDefinedStopIndex + stopOffset) / (nextDefinedStopIndex - previousDefinedStopIndex + stopOffset); } else { previousDefinedStopIndex = i; } offsets[i] = Math.max(0, Math.min(1, (stop - d0) / (d1 - d0))); } let lastDefinedColor = fills.find((c) => c.color != null)?.color; let colorScale; const colorStops = fills.map(({ color }, i) => { const offset4 = offsets[i]; if (color != null) { lastDefinedColor = color; } else if (lastDefinedColor != null) { color = lastDefinedColor; } else { if (colorScale == null) { colorScale = new ColorScale(); colorScale.domain = [0, 1]; colorScale.range = defaultColorStops; } color = colorScale.convert(offset4); } return { offset: offset4, color }; }); return fillMode === "discrete" ? discreteColorStops(colorStops) : colorStops; } // packages/ag-charts-community/src/scene/util/fill.ts function isGradientFill(fill) { return fill !== null && isObject(fill) && fill.type == "gradient"; } // packages/ag-charts-community/src/scene/util/pixel.ts function align(pixelRatio, start, length) { const alignedStart = Math.round(start * pixelRatio) / pixelRatio; if (length == null) { return alignedStart; } else if (length === 0) { return 0; } else if (length < 1) { return Math.ceil(length * pixelRatio) / pixelRatio; } return Math.round((length + start) * pixelRatio) / pixelRatio - alignedStart; } function alignBefore(pixelRatio, start) { return Math.floor(start * pixelRatio) / pixelRatio; } // packages/ag-charts-community/src/scene/shape/shape.ts var LINEAR_GRADIENT_REGEXP = /^linear-gradient\((-?[\d.]+)deg,(.*?)\)$/i; var _Shape = class _Shape extends Node { constructor() { super(...arguments); this.fillOpacity = 1; this.strokeOpacity = 1; this.fill = _Shape.defaultStyles.fill; this.stroke = _Shape.defaultStyles.stroke; this.strokeWidth = _Shape.defaultStyles.strokeWidth; this.lineDash = _Shape.defaultStyles.lineDash; this.lineDashOffset = _Shape.defaultStyles.lineDashOffset; this.lineCap = _Shape.defaultStyles.lineCap; this.lineJoin = _Shape.defaultStyles.lineJoin; this.miterLimit = void 0; this.opacity = _Shape.defaultStyles.opacity; this.fillShadow = _Shape.defaultStyles.fillShadow; this.gradientFillOptions = { domain: [0, 1], defaultColorRange: _Shape.defaultStyles.defaultColorRange }; } /** * Restores the default styles introduced by this subclass. */ restoreOwnStyles() { const { defaultStyles } = this.constructor; Object.assign(this, defaultStyles); } getGradient(pattern) { let linearGradientMatch; if (pattern instanceof Gradient) { return pattern; } else if (typeof pattern === "string" && pattern?.startsWith("linear-gradient") && (linearGradientMatch = LINEAR_GRADIENT_REGEXP.exec(pattern))) { const angle = parseFloat(linearGradientMatch[1]); const colors = []; const colorsPart = linearGradientMatch[2]; const colorRegex = /(#[0-9a-f]+)|(rgba?\(.+?\))|([a-z]+)/gi; let c; while (c = colorRegex.exec(colorsPart)) { colors.push(c[0]); } return new LinearGradient( "rgb", colors.map((color, index) => ({ color, offset: index / (colors.length - 1) })), angle ); } else if (isGradientFill(pattern)) { return this.createLinearGradient(pattern); } return void 0; } createLinearGradient(fill) { const { colorStops = [], direction } = fill; const isHorizontal = direction === "horizontal"; const { domain, defaultColorRange = [] } = this.gradientFillOptions; const stops = getColorStops(colorStops, defaultColorRange, domain); return new LinearGradient("oklch", stops, isHorizontal ? 0 : 90); } onFillChange() { this.fillGradient = this.getGradient(this.fill); } onStrokeChange() { this.strokeGradient = this.getGradient(this.stroke); } /** * Returns a device-pixel aligned coordinate (or length if length is supplied). * * NOTE: Not suitable for strokes, since the stroke needs to be offset to the middle * of a device pixel. */ align(start, length) { return align(this.layerManager?.canvas?.pixelRatio ?? 1, start, length); } fillStroke(ctx, path) { this.renderFill(ctx, path); this.renderStroke(ctx, path); } renderFill(ctx, path) { if (this.fill) { const { globalAlpha } = ctx; this.applyFill(ctx); this.applyFillAlpha(ctx); this.applyShadow(ctx); this.executeFill(ctx, path); ctx.globalAlpha = globalAlpha; } ctx.shadowColor = "rgba(0, 0, 0, 0)"; } executeFill(ctx, path) { if (path) { ctx.fill(path); } else { ctx.fill(); } } applyFill(ctx) { const bbox = this.gradientFillOptions.bbox ?? this.getBBox(); ctx.fillStyle = this.fillGradient?.createGradient(ctx, bbox) ?? (typeof this.fill === "string" ? this.fill : void 0) ?? "black"; } applyStroke(ctx) { ctx.strokeStyle = this.strokeGradient?.createGradient(ctx, this.getBBox()) ?? (typeof this.stroke === "string" ? this.stroke : void 0) ?? "black"; } applyFillAlpha(ctx) { ctx.globalAlpha *= this.opacity * this.fillOpacity; } applyShadow(ctx) { const pixelRatio = this.layerManager?.canvas.pixelRatio ?? 1; const fillShadow = this.fillShadow; if (fillShadow?.enabled) { ctx.shadowColor = fillShadow.color; ctx.shadowOffsetX = fillShadow.xOffset * pixelRatio; ctx.shadowOffsetY = fillShadow.yOffset * pixelRatio; ctx.shadowBlur = fillShadow.blur * pixelRatio; } } renderStroke(ctx, path) { if (this.stroke && this.strokeWidth) { const { globalAlpha } = ctx; this.applyStroke(ctx); ctx.globalAlpha *= this.opacity * this.strokeOpacity; ctx.lineWidth = this.strokeWidth; if (this.lineDash) { ctx.setLineDash(this.lineDash); } if (this.lineDashOffset) { ctx.lineDashOffset = this.lineDashOffset; } if (this.lineCap) { ctx.lineCap = this.lineCap; } if (this.lineJoin) { ctx.lineJoin = this.lineJoin; } if (this.miterLimit != null) { ctx.miterLimit = this.miterLimit; } this.executeStroke(ctx, path); ctx.globalAlpha = globalAlpha; } } executeStroke(ctx, path) { if (path) { ctx.stroke(path); } else { ctx.stroke(); } } containsPoint(x, y) { return this.isPointInPath(x, y); } applySvgFillAttributes(element) { const { fill, fillOpacity } = this; element.setAttribute("fill", typeof fill === "string" ? fill : "none"); element.setAttribute("fill-opacity", String(fillOpacity)); } applySvgStrokeAttributes(element) { const { stroke, strokeOpacity, strokeWidth, lineDash, lineDashOffset } = this; if (stroke != null) { element.setAttribute("stroke", typeof stroke === "string" ? stroke : "none"); element.setAttribute("stroke-opacity", String(strokeOpacity)); element.setAttribute("stroke-width", String(strokeWidth)); } if (lineDash?.some((d) => d !== 0) === true) { const svgLineDash = lineDash.length % 2 === 1 ? [...lineDash, ...lineDash] : lineDash; element.setAttribute("stroke-dasharray", svgLineDash.join(" ")); element.setAttribute("stroke-dashoffset", String(lineDashOffset)); } } }; /** * Defaults for style properties. Note that properties that affect the position * and shape of the node are not considered style properties, for example: * `x`, `y`, `width`, `height`, `radius`, `rotation`, etc. * Can be used to reset to the original styling after some custom styling * has been applied (using the `restoreOwnStyles` method). * These static defaults are meant to be inherited by subclasses. */ _Shape.defaultStyles = { fill: "black", stroke: void 0, strokeWidth: 0, lineDash: void 0, lineDashOffset: 0, lineCap: void 0, lineJoin: void 0, opacity: 1, fillShadow: void 0, defaultColorRange: ["#5090dc", "#ef5452"] }; __decorateClass([ SceneChangeDetection() ], _Shape.prototype, "fillOpacity", 2); __decorateClass([ SceneChangeDetection() ], _Shape.prototype, "strokeOpacity", 2); __decorateClass([ SceneChangeDetection({ changeCb: (s) => s.onFillChange() }) ], _Shape.prototype, "fill", 2); __decorateClass([ SceneChangeDetection({ changeCb: (s) => s.onStrokeChange() }) ], _Shape.prototype, "stroke", 2); __decorateClass([ SceneChangeDetection() ], _Shape.prototype, "strokeWidth", 2); __decorateClass([ SceneChangeDetection() ], _Shape.prototype, "lineDash", 2); __decorateClass([ SceneChangeDetection() ], _Shape.prototype, "lineDashOffset", 2); __decorateClass([ SceneChangeDetection() ], _Shape.prototype, "lineCap", 2); __decorateClass([ SceneChangeDetection() ], _Shape.prototype, "lineJoin", 2); __decorateClass([ SceneChangeDetection() ], _Shape.prototype, "miterLimit", 2); __decorateClass([ SceneChangeDetection({ convertor: (v) => clamp(0, v, 1) }) ], _Shape.prototype, "opacity", 2); __decorateClass([ SceneChangeDetection({ checkDirtyOnAssignment: true }) ], _Shape.prototype, "fillShadow", 2); __decorateClass([ SceneChangeDetection({ changeCb: (s) => s.onFillChange() }) ], _Shape.prototype, "gradientFillOptions", 2); var Shape = _Shape; // packages/ag-charts-community/src/scene/shape/text.ts var _Text = class _Text extends Shape { constructor() { super(...arguments); this.x = 0; this.y = 0; this.lines = []; this.text = void 0; this.fontSize = 10; this.fontFamily = "sans-serif"; this.textAlign = _Text.defaultStyles.textAlign; this.textBaseline = _Text.defaultStyles.textBaseline; } onTextChange() { this.lines = this.text?.split("\n").map((s) => s.trim()) ?? []; } static computeBBox(lines, x, y, opts) { const { offsetTop, offsetLeft, width, height } = CachedTextMeasurerPool.measureLines(lines, opts); return new BBox(x - offsetLeft, y - offsetTop, width, height); } computeBBox() { const { x, y, lines, textBaseline, textAlign } = this; return _Text.computeBBox(lines, x, y, { font: this, textBaseline, textAlign }); } isPointInPath(x, y) { const bbox = this.getBBox(); return bbox ? bbox.containsPoint(x, y) : false; } render(renderCtx) { const { ctx, stats } = renderCtx; if (!this.lines.length || !this.layerManager) { if (stats) stats.nodesSkipped += 1; return super.render(renderCtx); } const { fill, stroke, strokeWidth } = this; const { pixelRatio } = this.layerManager.canvas; ctx.font = TextUtils.toFontString(this); ctx.textAlign = this.textAlign; ctx.textBaseline = this.textBaseline; if (fill) { this.applyFill(ctx); ctx.globalAlpha *= this.opacity * this.fillOpacity; const { fillShadow } = this; if (fillShadow?.enabled) { ctx.shadowColor = fillShadow.color; ctx.shadowOffsetX = fillShadow.xOffset * pixelRatio; ctx.shadowOffsetY = fillShadow.yOffset * pixelRatio; ctx.shadowBlur = fillShadow.blur * pixelRatio; } this.renderLines((line, x, y) => ctx.fillText(line, x, y)); } if (stroke && strokeWidth) { this.applyStroke(ctx); ctx.lineWidth = strokeWidth; ctx.globalAlpha *= this.opacity * this.strokeOpacity; const { lineDash, lineDashOffset, lineCap, lineJoin } = this; if (lineDash) { ctx.setLineDash(lineDash); } if (lineDashOffset) { ctx.lineDashOffset = lineDashOffset; } if (lineCap) { ctx.lineCap = lineCap; } if (lineJoin) { ctx.lineJoin = lineJoin; } this.renderLines((line, x, y) => ctx.strokeText(line, x, y)); } super.render(renderCtx); } renderLines(renderCallback) { const { lines, x, y } = this; const lineHeight = this.lineHeight ?? TextUtils.getLineHeight(this.fontSize); let offsetY = (lineHeight - lineHeight * lines.length) * TextUtils.getVerticalModifier(this.textBaseline); for (const line of lines) { renderCallback(line, x, y + offsetY); offsetY += lineHeight; } } setFont(props) { this.fontFamily = props.fontFamily; this.fontSize = props.fontSize; this.fontStyle = props.fontStyle; this.fontWeight = props.fontWeight; } setAlign(props) { this.textAlign = props.textAlign; this.textBaseline = props.textBaseline; } toSVG() { if (!this.visible || !this.text) return; const element = createSvgElement("text"); this.applySvgFillAttributes(element); element.setAttribute("font-family", this.fontFamily?.split(",")[0] ?? ""); element.setAttribute("font-size", String(this.fontSize)); element.setAttribute("font-style", this.fontStyle ?? ""); element.setAttribute("font-weight", String(this.fontWeight ?? "")); element.setAttribute( "text-anchor", { center: "middle", left: "start", right: "end", start: "start", end: "end" }[this.textAlign ?? "start"] ); element.setAttribute( "alignment-baseline", { alphabetic: "alphabetic", top: "top", bottom: "bottom", hanging: "hanging", middle: "middle", ideographic: "ideographic" }[this.textBaseline ?? "alphabetic"] ); element.setAttribute("x", String(this.x)); element.setAttribute("y", String(this.y)); element.textContent = this.text ?? ""; return { elements: [element] }; } }; _Text.className = "Text"; _Text.defaultStyles = { ...Shape.defaultStyles, textAlign: "start", fontStyle: void 0, fontWeight: void 0, fontSize: 10, fontFamily: "sans-serif", textBaseline: "alphabetic" }; __decorateClass([ SceneChangeDetection() ], _Text.prototype, "x", 2); __decorateClass([ SceneChangeDetection() ], _Text.prototype, "y", 2); __decorateClass([ SceneChangeDetection({ changeCb: (o) => o.onTextChange() }) ], _Text.prototype, "text", 2); __decorateClass([ SceneChangeDetection() ], _Text.prototype, "fontStyle", 2); __decorateClass([ SceneChangeDetection() ], _Text.prototype, "fontWeight", 2); __decorateClass([ SceneChangeDetection() ], _Text.prototype, "fontSize", 2); __decorateClass([ SceneChangeDetection() ], _Text.prototype, "fontFamily", 2); __decorateClass([ SceneChangeDetection() ], _Text.prototype, "textAlign", 2); __decorateClass([ SceneChangeDetection() ], _Text.prototype, "textBaseline", 2); __decorateClass([ SceneChangeDetection() ], _Text.prototype, "lineHeight", 2); var Text = _Text; var RotatableText = class extends Rotatable(Text) { }; var TransformableText = class extends Rotatable(Translatable(Text)) { }; // packages/ag-charts-community/src/util/proxy.ts function ProxyPropertyOnWrite(childName, childProperty) { return addTransformToInstanceProperty((target, key, value) => target[childName][childProperty ?? key] = value); } function ObserveChanges(observerFn) { return addObserverToInstanceProperty(observerFn); } // packages/ag-charts-community/src/util/textWrapper.ts var TextWrapper = class { static wrapText(text, options) { return this.wrapLines(text, options).join("\n"); } static wrapLines(text, options) { const clippedResult = this.textWrap(text, options); if (options.overflow === "hide" && clippedResult.some((l) => l.endsWith(TextUtils.EllipsisChar))) { return []; } return clippedResult; } static appendEllipsis(text) { return text.replace(/[.,]{1,5}$/, "") + TextUtils.EllipsisChar; } static truncateLine(text, measurer, maxWidth, ellipsisForce) { const ellipsisWidth = measurer.textWidth(TextUtils.EllipsisChar); let estimatedWidth = 0; let i = 0; for (; i < text.length; i++) { const charWidth = measurer.textWidth(text.charAt(i)); if (estimatedWidth + charWidth > maxWidth) break; estimatedWidth += charWidth; } if (text.length === i && (!ellipsisForce || estimatedWidth + ellipsisWidth <= maxWidth)) { return ellipsisForce ? text + TextUtils.EllipsisChar : text; } text = text.slice(0, i).trimEnd(); while (text.length && measurer.textWidth(text) + ellipsisWidth > maxWidth) { text = text.slice(0, -1).trimEnd(); } return text + TextUtils.EllipsisChar; } static textWrap(text, options) { const lines = text.split(TextUtils.lineSplitter); const measurer = CachedTextMeasurerPool.getMeasurer(options); if (options.textWrap === "never") { return lines.map((line) => this.truncateLine(line.trimEnd(), measurer, options.maxWidth)); } const result = []; const wrapHyphenate = options.textWrap === "hyphenate"; const wrapOnSpace = options.textWrap == null || options.textWrap === "on-space"; for (const untrimmedLine of lines) { let line = untrimmedLine.trimEnd(); if (line === "") { result.push(line); continue; } let i = 0; let estimatedWidth = 0; let lastSpaceIndex = 0; while (i < line.length) { const char = line.charAt(i); estimatedWidth += measurer.textWidth(char); if (char === " ") { lastSpaceIndex = i; } if (estimatedWidth > options.maxWidth) { if (i === 0) break; const actualWidth = measurer.textWidth(line.slice(0, i + 1)); if (actualWidth <= options.maxWidth) { estimatedWidth = actualWidth; i++; continue; } if (lastSpaceIndex) { const nextWord = this.getWordAt(line, lastSpaceIndex + 1); const textWidth = measurer.textWidth(nextWord); if (textWidth <= options.maxWidth) { result.push(line.slice(0, lastSpaceIndex).trimEnd()); line = line.slice(lastSpaceIndex).trimStart(); i = 0; estimatedWidth = 0; lastSpaceIndex = 0; continue; } else if (wrapOnSpace && textWidth > options.maxWidth) { result.push( line.slice(0, lastSpaceIndex).trimEnd(), this.truncateLine( line.slice(lastSpaceIndex).trimStart(), measurer, options.maxWidth, true ) ); } } else if (wrapOnSpace) { result.push(this.truncateLine(line, measurer, options.maxWidth, true)); } if (wrapOnSpace) { line = ""; break; } const postfix = wrapHyphenate ? "-" : ""; let newLine = line.slice(0, i).trim(); while (newLine.length && measurer.textWidth(newLine + postfix) > options.maxWidth) { newLine = newLine.slice(0, -1).trimEnd(); } result.push(newLine + postfix); if (!newLine.length) { line = ""; break; } line = line.slice(newLine.length).trimStart(); i = -1; estimatedWidth = 0; lastSpaceIndex = 0; } i++; } if (line) { result.push(line); } } this.avoidOrphans(result, measurer, options); return this.clipLines(result, measurer, options); } static getWordAt(text, position) { const nextSpaceIndex = text.indexOf(" ", position); return nextSpaceIndex === -1 ? text.slice(position) : text.slice(position, nextSpaceIndex); } static clipLines(lines, measurer, options) { if (!options.maxHeight) { return lines; } const { height, lineMetrics } = measurer.measureLines(lines); if (height <= options.maxHeight) { return lines; } for (let i = 0, cumulativeHeight = 0; i < lineMetrics.length; i++) { const { lineHeight } = lineMetrics[i]; cumulativeHeight += lineHeight; if (cumulativeHeight > options.maxHeight) { if (options.overflow === "hide") { return []; } const clippedResults = lines.slice(0, i || 1); const lastLine = clippedResults.pop(); return clippedResults.concat(this.truncateLine(lastLine, measurer, options.maxWidth, true)); } } return lines; } static avoidOrphans(lines, measurer, options) { if (options.avoidOrphans === false || lines.length < 2) return; const { length } = lines; const lastLine = lines[length - 1]; const beforeLast = lines[length - 2]; if (beforeLast.length < lastLine.length) return; const lastSpaceIndex = beforeLast.lastIndexOf(" "); if (lastSpaceIndex === -1 || lastSpaceIndex === beforeLast.indexOf(" ") || lastLine.includes(" ")) return; const lastWord = beforeLast.slice(lastSpaceIndex + 1); if (measurer.textWidth(lastLine + lastWord) <= options.maxWidth) { lines[length - 2] = beforeLast.slice(0, lastSpaceIndex); lines[length - 1] = lastWord + " " + lastLine; } } }; // packages/ag-charts-community/src/chart/caption.ts var Caption = class extends BaseProperties { constructor() { super(...arguments); this.id = createId(this); this.node = new RotatableText({ zIndex: 1 }).setProperties({ textAlign: "center", pointerEvents: 1 /* None */ }); this.enabled = false; this.textAlign = "center"; this.fontSize = 10; this.fontFamily = "sans-serif"; this.wrapping = "always"; this.padding = 0; this.layoutStyle = "block"; this.truncated = false; } registerInteraction(moduleCtx, where) { return moduleCtx.layoutManager.addListener("layout:complete", () => this.updateA11yText(moduleCtx, where)); } computeTextWrap(containerWidth, containerHeight) { const { text, padding, wrapping } = this; const maxWidth = Math.min(this.maxWidth ?? Infinity, containerWidth) - padding * 2; const maxHeight = this.maxHeight ?? containerHeight - padding * 2; if (!isFinite(maxWidth) && !isFinite(maxHeight)) { this.node.text = text; return; } const wrappedText = TextWrapper.wrapText(text ?? "", { maxWidth, maxHeight, font: this, textWrap: wrapping }); this.node.text = wrappedText; this.truncated = wrappedText.includes(TextUtils.EllipsisChar); } updateA11yText(moduleCtx, where) { const { proxyInteractionService } = moduleCtx; if (this.enabled && this.text) { const bbox = Transformable.toCanvas(this.node); if (bbox) { const { id: domManagerId } = this; this.proxyText ?? (this.proxyText = proxyInteractionService.createProxyElement({ type: "text", domManagerId, where })); this.proxyText.textContent = this.text; this.proxyText.setBounds(bbox); this.proxyText.addListener("mousemove", (ev) => this.handleMouseMove(moduleCtx, ev)); this.proxyText.addListener("mouseleave", (ev) => this.handleMouseLeave(moduleCtx, ev)); } } else { this.proxyText?.destroy(); this.proxyText = void 0; } } handleMouseMove(moduleCtx, event) { if (event != null && this.enabled && this.node.visible && this.truncated) { const { x, y } = Transformable.toCanvas(this.node); const canvasX = event.sourceEvent.offsetX + x; const canvasY = event.sourceEvent.offsetY + y; const lastPointerEvent = { type: "pointermove", canvasX, canvasY }; moduleCtx.tooltipManager.updateTooltip( this.id, { canvasX, canvasY, lastPointerEvent, showArrow: false }, { type: "structured", title: this.text } ); } } handleMouseLeave(moduleCtx, _event) { moduleCtx.tooltipManager.removeTooltip(this.id); } }; Caption.SMALL_PADDING = 10; Caption.LARGE_PADDING = 20; __decorateClass([ Validate(BOOLEAN), ProxyPropertyOnWrite("node", "visible") ], Caption.prototype, "enabled", 2); __decorateClass([ Validate(STRING, { optional: true }), ProxyPropertyOnWrite("node") ], Caption.prototype, "text", 2); __decorateClass([ Validate(TEXT_ALIGN, { optional: true }), ProxyPropertyOnWrite("node") ], Caption.prototype, "textAlign", 2); __decorateClass([ Validate(FONT_STYLE, { optional: true }), ProxyPropertyOnWrite("node") ], Caption.prototype, "fontStyle", 2); __decorateClass([ Validate(FONT_WEIGHT, { optional: true }), ProxyPropertyOnWrite("node") ], Caption.prototype, "fontWeight", 2); __decorateClass([ Validate(POSITIVE_NUMBER), ProxyPropertyOnWrite("node") ], Caption.prototype, "fontSize", 2); __decorateClass([ Validate(STRING), ProxyPropertyOnWrite("node") ], Caption.prototype, "fontFamily", 2); __decorateClass([ Validate(COLOR_STRING, { optional: true }), ProxyPropertyOnWrite("node", "fill") ], Caption.prototype, "color", 2); __decorateClass([ Validate(POSITIVE_NUMBER, { optional: true }) ], Caption.prototype, "spacing", 2); __decorateClass([ Validate(POSITIVE_NUMBER, { optional: true }) ], Caption.prototype, "maxWidth", 2); __decorateClass([ Validate(POSITIVE_NUMBER, { optional: true }) ], Caption.prototype, "maxHeight", 2); __decorateClass([ Validate(TEXT_WRAP) ], Caption.prototype, "wrapping", 2); __decorateClass([ Validate(POSITIVE_NUMBER) ], Caption.prototype, "padding", 2); __decorateClass([ Validate(STRING) ], Caption.prototype, "layoutStyle", 2); // packages/ag-charts-community/src/util/distance.ts function pointsDistanceSquared(x1, y1, x2, y2) { const dx = x1 - x2; const dy = y1 - y2; return dx * dx + dy * dy; } function lineDistanceSquared(x, y, x1, y1, x2, y2, best) { if (x1 === x2 && y1 === y2) { return Math.min(best, pointsDistanceSquared(x, y, x1, y1)); } const dx = x2 - x1; const dy = y2 - y1; const t = Math.max(0, Math.min(1, ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy))); const ix = x1 + t * dx; const iy = y1 + t * dy; return Math.min(best, pointsDistanceSquared(x, y, ix, iy)); } function arcDistanceSquared(x, y, cx, cy, radius, startAngle, endAngle, counterClockwise, best) { if (counterClockwise) { [endAngle, startAngle] = [startAngle, endAngle]; } const angle = Math.atan2(y - cy, x - cx); if (!isBetweenAngles(angle, startAngle, endAngle)) { const startX = cx + Math.cos(startAngle) * radius; const startY = cy + Math.sin(startAngle) * radius; const endX = cx + Math.cos(startAngle) * radius; const endY = cy + Math.sin(startAngle) * radius; return Math.min(best, pointsDistanceSquared(x, y, startX, startY), pointsDistanceSquared(x, y, endX, endY)); } const distToArc = radius - Math.sqrt(pointsDistanceSquared(x, y, cx, cy)); return Math.min(best, distToArc * distToArc); } // packages/ag-charts-community/src/scene/polyRoots.ts function linearRoot(a, b) { const t = -b / a; return a !== 0 && t >= 0 && t <= 1 ? [t] : []; } function quadraticRoots(a, b, c) { if (a === 0) { return linearRoot(b, c); } const D = b * b - 4 * a * c; const roots = []; if (D === 0) { const t = -b / (2 * a); if (t >= 0 && t <= 1) { roots.push(t); } } else if (D > 0) { const rD = Math.sqrt(D); const t1 = (-b - rD) / (2 * a); const t2 = (-b + rD) / (2 * a); if (t1 >= 0 && t1 <= 1) { roots.push(t1); } if (t2 >= 0 && t2 <= 1) { roots.push(t2); } } return roots; } function cubicRoots(a, b, c, d) { if (a === 0) { return quadraticRoots(b, c, d); } const A = b / a; const B = c / a; const C = d / a; const Q = (3 * B - A * A) / 9; const R = (9 * A * B - 27 * C - 2 * A * A * A) / 54; const D = Q * Q * Q + R * R; const third = 1 / 3; const roots = []; if (D >= 0) { const rD = Math.sqrt(D); const S = Math.sign(R + rD) * Math.pow(Math.abs(R + rD), third); const T = Math.sign(R - rD) * Math.pow(Math.abs(R - rD), third); const Im = Math.abs(Math.sqrt(3) * (S - T) / 2); const t = -third * A + (S + T); if (t >= 0 && t <= 1) { roots.push(t); } if (Im === 0) { const t2 = -third * A - (S + T) / 2; if (t2 >= 0 && t2 <= 1) { roots.push(t2); } } } else { const theta = Math.acos(R / Math.sqrt(-Q * Q * Q)); const thirdA = third * A; const twoSqrtQ = 2 * Math.sqrt(-Q); const t1 = twoSqrtQ * Math.cos(third * theta) - thirdA; const t2 = twoSqrtQ * Math.cos(third * (theta + 2 * Math.PI)) - thirdA; const t3 = twoSqrtQ * Math.cos(third * (theta + 4 * Math.PI)) - thirdA; if (t1 >= 0 && t1 <= 1) { roots.push(t1); } if (t2 >= 0 && t2 <= 1) { roots.push(t2); } if (t3 >= 0 && t3 <= 1) { roots.push(t3); } } return roots; } // packages/ag-charts-community/src/scene/intersection.ts function segmentIntersection(ax1, ay1, ax2, ay2, bx1, by1, bx2, by2) { const d = (ax2 - ax1) * (by2 - by1) - (ay2 - ay1) * (bx2 - bx1); if (d === 0) { return 0; } const ua = ((bx2 - bx1) * (ay1 - by1) - (ax1 - bx1) * (by2 - by1)) / d; const ub = ((ax2 - ax1) * (ay1 - by1) - (ay2 - ay1) * (ax1 - bx1)) / d; if (ua >= 0 && ua <= 1 && ub >= 0 && ub <= 1) { return 1; } return 0; } function cubicSegmentIntersections(px1, py1, px2, py2, px3, py3, px4, py4, x1, y1, x2, y2) { let intersections = 0; const A = y1 - y2; const B = x2 - x1; const C = x1 * (y2 - y1) - y1 * (x2 - x1); const bx = bezierCoefficients(px1, px2, px3, px4); const by = bezierCoefficients(py1, py2, py3, py4); const a = A * bx[0] + B * by[0]; const b = A * bx[1] + B * by[1]; const c = A * bx[2] + B * by[2]; const d = A * bx[3] + B * by[3] + C; const roots = cubicRoots(a, b, c, d); for (const t of roots) { const tt = t * t; const ttt = t * tt; const x = bx[0] * ttt + bx[1] * tt + bx[2] * t + bx[3]; const y = by[0] * ttt + by[1] * tt + by[2] * t + by[3]; let s; if (x1 === x2) { s = (y - y1) / (y2 - y1); } else { s = (x - x1) / (x2 - x1); } if (s >= 0 && s <= 1) { intersections++; } } return intersections; } function bezierCoefficients(P1, P2, P3, P4) { return [ // Bézier expressed as matrix operations: -P1 + 3 * P2 - 3 * P3 + P4, // |-1 3 -3 1| |P1| 3 * P1 - 6 * P2 + 3 * P3, // [t^3 t^2 t 1] | 3 -6 3 0| |P2| -3 * P1 + 3 * P2, // |-3 3 0 0| |P3| P1 // | 1 0 0 0| |P4| ]; } function arcIntersections(cx, cy, r, startAngle, endAngle, counterClockwise, x1, y1, x2, y2) { if (isNaN(cx) || isNaN(cy)) { return 0; } if (counterClockwise) { [endAngle, startAngle] = [startAngle, endAngle]; } const k = (y2 - y1) / (x2 - x1); const y0 = y1 - k * x1; const a = Math.pow(k, 2) + 1; const b = 2 * (k * (y0 - cy) - cx); const c = Math.pow(cx, 2) + Math.pow(y0 - cy, 2) - Math.pow(r, 2); const d = Math.pow(b, 2) - 4 * a * c; if (d < 0) { return 0; } const i1x = (-b + Math.sqrt(d)) / 2 / a; const i2x = (-b - Math.sqrt(d)) / 2 / a; let intersections = 0; [i1x, i2x].forEach((x) => { const isXInsideLine = x >= Math.min(x1, x2) && x <= Math.max(x1, x2); if (!isXInsideLine) { return; } const y = k * x + y0; const adjacent = x - cx; const opposite = y - cy; const angle = Math.atan2(opposite, adjacent); if (isBetweenAngles(angle, startAngle, endAngle)) { intersections++; } }); return intersections; } // packages/ag-charts-community/src/scene/util/bezier.ts function evaluateBezier(p0, p1, p2, p3, t) { return (1 - t) ** 3 * p0 + 3 * (1 - t) ** 2 * t * p1 + 3 * (1 - t) * t ** 2 * p2 + t ** 3 * p3; } function calculateDerivativeExtrema(p0, p1, p2, p3) { const a = -p0 + 3 * p1 - 3 * p2 + p3; const b = 3 * p0 - 6 * p1 + 3 * p2; const c = -3 * p0 + 3 * p1; if (a === 0) { if (b !== 0) { const t = -c / b; if (t > 0 && t < 1) { return [t]; } } return []; } const discriminant = b * b - 4 * a * c; if (discriminant >= 0) { const sqrtDiscriminant = Math.sqrt(discriminant); const t1 = (-b + sqrtDiscriminant) / (2 * a); const t2 = (-b - sqrtDiscriminant) / (2 * a); return [t1, t2].filter((t) => t > 0 && t < 1); } return []; } function calculateDerivativeExtremaXY(sx, sy, cp1x, cp1y, cp2x, cp2y, x, y) { const tx = calculateDerivativeExtrema(sx, cp1x, cp2x, x); const ty = calculateDerivativeExtrema(sy, cp1y, cp2y, y); return [...tx, ...ty]; } // packages/ag-charts-community/src/scene/extendedPath2D.ts var ExtendedPath2D = class { constructor() { // The methods of this class will likely be called many times per animation frame, // and any allocation can trigger a GC cycle during animation, so we attempt // to minimize the number of allocations. this.path2d = new Path2D(); this.previousCommands = []; this.previousParams = []; this.previousClosedPath = false; this.commands = []; this.params = []; this.openedPath = false; this.closedPath = false; } isEmpty() { return this.commands.length === 0; } isDirty() { return this.closedPath !== this.previousClosedPath || this.previousCommands.length !== this.commands.length || this.previousParams.length !== this.params.length || this.previousCommands.toString() !== this.commands.toString() || this.previousParams.toString() !== this.params.toString(); } getPath2D() { return this.path2d; } moveTo(x, y) { this.openedPath = true; this.path2d.moveTo(x, y); this.commands.push(0 /* Move */); this.params.push(x, y); } lineTo(x, y) { if (this.openedPath) { this.path2d.lineTo(x, y); this.commands.push(1 /* Line */); this.params.push(x, y); } else { this.moveTo(x, y); } } rect(x, y, width, height) { this.moveTo(x, y); this.lineTo(x + width, y); this.lineTo(x + width, y + height); this.lineTo(x, y + height); this.closePath(); } roundRect(x, y, width, height, radii) { radii = Math.min(radii, width / 2, height / 2); this.moveTo(x, y + radii); this.arc(x + radii, y + radii, radii, Math.PI, 1.5 * Math.PI); this.lineTo(x + radii, y); this.lineTo(x + width - radii, y); this.arc(x + width - radii, y + radii, radii, 1.5 * Math.PI, 2 * Math.PI); this.lineTo(x + width, y + radii); this.lineTo(x + width, y + height - radii); this.arc(x + width - radii, y + height - radii, radii, 0, Math.PI / 2); this.lineTo(x + width - radii, y + height); this.lineTo(x + radii, y + height); this.arc(x + +radii, y + height - radii, radii, Math.PI / 2, Math.PI); this.lineTo(x, y + height - radii); this.closePath(); } arc(x, y, r, sAngle, eAngle, counterClockwise) { this.openedPath = true; this.path2d.arc(x, y, r, sAngle, eAngle, counterClockwise); this.commands.push(2 /* Arc */); this.params.push(x, y, r, sAngle, eAngle, counterClockwise ? 1 : 0); } cubicCurveTo(cx1, cy1, cx2, cy2, x, y) { if (!this.openedPath) { this.moveTo(cx1, cy1); } this.path2d.bezierCurveTo(cx1, cy1, cx2, cy2, x, y); this.commands.push(3 /* Curve */); this.params.push(cx1, cy1, cx2, cy2, x, y); } closePath() { if (this.openedPath) { this.path2d.closePath(); this.commands.push(4 /* ClosePath */); this.openedPath = false; this.closedPath = true; } } clear(trackChanges) { if (trackChanges) { this.previousCommands = this.commands; this.previousParams = this.params; this.previousClosedPath = this.closedPath; } this.path2d = new Path2D(); this.openedPath = false; this.closedPath = false; this.commands = []; this.params = []; } isPointInPath(x, y) { const commands = this.commands; const params = this.params; const cn = commands.length; const ox = -1e4; const oy = -1e4; let sx = NaN; let sy = NaN; let px = 0; let py = 0; let intersectionCount = 0; for (let ci = 0, pi = 0; ci < cn; ci++) { switch (commands[ci]) { case 0 /* Move */: intersectionCount += segmentIntersection(sx, sy, px, py, ox, oy, x, y); px = params[pi++]; sx = px; py = params[pi++]; sy = py; break; case 1 /* Line */: intersectionCount += segmentIntersection(px, py, params[pi++], params[pi++], ox, oy, x, y); px = params[pi - 2]; py = params[pi - 1]; break; case 3 /* Curve */: intersectionCount += cubicSegmentIntersections( px, py, params[pi++], params[pi++], params[pi++], params[pi++], params[pi++], params[pi++], ox, oy, x, y ); px = params[pi - 2]; py = params[pi - 1]; break; case 2 /* Arc */: { const cx = params[pi++]; const cy = params[pi++]; const r = params[pi++]; const startAngle = params[pi++]; const endAngle = params[pi++]; const counterClockwise = Boolean(params[pi++]); intersectionCount += arcIntersections( cx, cy, r, startAngle, endAngle, counterClockwise, ox, oy, x, y ); if (!isNaN(sx)) { const startX = cx + Math.cos(startAngle) * r; const startY = cy + Math.sin(startAngle) * r; intersectionCount += segmentIntersection(px, py, startX, startY, ox, oy, x, y); } px = cx + Math.cos(endAngle) * r; py = cy + Math.sin(endAngle) * r; break; } case 4 /* ClosePath */: intersectionCount += segmentIntersection(sx, sy, px, py, ox, oy, x, y); break; } } return intersectionCount % 2 === 1; } distanceSquared(x, y) { let best = Infinity; const commands = this.commands; const params = this.params; const cn = commands.length; let sx = NaN; let sy = NaN; let px = 0; let py = 0; for (let ci = 0, pi = 0; ci < cn; ci++) { switch (commands[ci]) { case 0 /* Move */: px = sx = params[pi++]; py = sy = params[pi++]; break; case 1 /* Line */: { const nx = params[pi++]; const ny = params[pi++]; best = lineDistanceSquared(x, y, px, py, nx, ny, best); break; } case 3 /* Curve */: logger_exports.error("Command.Curve distanceSquare not implemented"); break; case 2 /* Arc */: { const cx = params[pi++]; const cy = params[pi++]; const r = params[pi++]; const startAngle = params[pi++]; const endAngle = params[pi++]; const startX = cx + Math.cos(startAngle) * r; const startY = cy + Math.sin(startAngle) * r; const counterClockwise = Boolean(params[pi++]); best = lineDistanceSquared(x, y, px, py, startX, startY, best); best = arcDistanceSquared(x, y, cx, cy, r, startAngle, endAngle, counterClockwise, best); px = cx + Math.cos(endAngle) * r; py = cy + Math.sin(endAngle) * r; break; } case 4 /* ClosePath */: best = lineDistanceSquared(x, y, px, py, sx, sy, best); break; } } return best; } // https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d toSVG(transform = (x, y) => ({ x, y })) { const buffer = []; const { commands, params } = this; const addCommand = (command, ...points) => { buffer.push(command); for (let i = 0; i < points.length; i += 2) { const { x, y } = transform(points[i], points[i + 1]); buffer.push(x, y); } }; let pi = 0; for (const command of commands) { switch (command) { case 0 /* Move */: addCommand("M", params[pi++], params[pi++]); break; case 1 /* Line */: addCommand("L", params[pi++], params[pi++]); break; case 3 /* Curve */: addCommand("C", params[pi++], params[pi++], params[pi++], params[pi++], params[pi++], params[pi++]); break; case 2 /* Arc */: { const cx = params[pi++]; const cy = params[pi++]; const r = params[pi++]; const A0 = params[pi++]; const A1 = params[pi++]; const ccw = params[pi++]; let sweep = ccw ? A0 - A1 : A1 - A0; if (sweep < 0) { sweep += Math.ceil(-sweep / (2 * Math.PI)) * 2 * Math.PI; } if (ccw) { sweep = -sweep; } const arcSections = Math.max(Math.ceil(Math.abs(sweep) / (Math.PI / 2)), 1); const step = sweep / arcSections; const h = 4 / 3 * Math.tan(step / 4); const move = buffer.length === 0 ? "M" : "L"; addCommand(move, cx + Math.cos(A0) * r, cy + Math.sin(A0) * r); for (let i = 0; i < arcSections; i += 1) { const a0 = A0 + step * (i + 0); const a1 = A0 + step * (i + 1); const rSinStart = r * Math.sin(a0); const rCosStart = r * Math.cos(a0); const rSinEnd = r * Math.sin(a1); const rCosEnd = r * Math.cos(a1); addCommand( "C", cx + rCosStart - h * rSinStart, cy + rSinStart + h * rCosStart, cx + rCosEnd + h * rSinEnd, cy + rSinEnd - h * rCosEnd, cx + rCosEnd, cy + rSinEnd ); } break; } case 4 /* ClosePath */: buffer.push("Z"); break; } } return buffer.join(" "); } computeBBox() { const { commands, params } = this; let [top, left, right, bot] = [Infinity, Infinity, -Infinity, -Infinity]; let [sx, sy] = [NaN, NaN]; let [mx, my] = [NaN, NaN]; const joinPoint = (x, y, updatestart) => { top = Math.min(y, top); left = Math.min(x, left); right = Math.max(x, right); bot = Math.max(y, bot); if (updatestart) { [sx, sy] = [x, y]; } }; let pi = 0; for (const command of commands) { switch (command) { case 0 /* Move */: joinPoint(params[pi++], params[pi++], true); [mx, my] = [sx, sy]; break; case 1 /* Line */: joinPoint(params[pi++], params[pi++], true); break; case 3 /* Curve */: { const cp1x = params[pi++]; const cp1y = params[pi++]; const cp2x = params[pi++]; const cp2y = params[pi++]; const x = params[pi++]; const y = params[pi++]; joinPoint(x, y, true); const Ts = calculateDerivativeExtremaXY(sx, sy, cp1x, cp1y, cp2x, cp2y, x, y); Ts.forEach((t) => { const px = evaluateBezier(sx, cp1x, cp2x, x, t); const py = evaluateBezier(sy, cp1y, cp2y, y, t); joinPoint(px, py); }); break; } case 2 /* Arc */: { const cx = params[pi++]; const cy = params[pi++]; const r = params[pi++]; let A0 = normalizeAngle360(params[pi++]); let A1 = normalizeAngle360(params[pi++]); const ccw = params[pi++]; if (ccw) { [A0, A1] = [A1, A0]; } const joinAngle = (angle, updatestart) => { const px = cx + r * Math.cos(angle); const py = cy + r * Math.sin(angle); joinPoint(px, py, updatestart); }; joinAngle(A0); joinAngle(A1, true); const criticalAngles = [0, Math.PI / 2, Math.PI, 3 * Math.PI / 2]; for (const crit of criticalAngles) { if (A0 < A1 && A0 <= crit && crit <= A1 || A0 > A1 && (A0 <= crit || crit <= A1)) { joinAngle(crit); } } break; } case 4 /* ClosePath */: [sx, sy] = [mx, my]; break; } } return new BBox(left, top, right - left, bot - top); } }; // packages/ag-charts-community/src/scene/shape/path.ts function ScenePathChangeDetection(opts) { const { changeCb, convertor } = opts ?? {}; return SceneChangeDetection({ type: "path", convertor, changeCb }); } var Path = class extends Shape { constructor() { super(...arguments); /** * Declare a path to retain for later rendering and hit testing * using custom Path2D class. Think of it as a TypeScript version * of the native Path2D (with some differences) that works in all browsers. */ this.path = new ExtendedPath2D(); this._clipX = NaN; this._clipY = NaN; this.clip = false; /** * The path only has to be updated when certain attributes change. * For example, if transform attributes (such as `translationX`) * are changed, we don't have to update the path. The `dirtyPath` flag * is how we keep track if the path has to be updated or not. */ this._dirtyPath = true; this.lastPixelRatio = NaN; } set clipX(value) { this._clipX = value; this.dirtyPath = true; } set clipY(value) { this._clipY = value; this.dirtyPath = true; } set dirtyPath(value) { if (this._dirtyPath !== value) { this._dirtyPath = value; if (value) { this.markDirty(); } } } get dirtyPath() { return this._dirtyPath; } checkPathDirty() { if (this._dirtyPath) { return; } this.dirtyPath = this.path.isDirty() || (this.fillShadow?.isDirty() ?? false) || (this._clipPath?.isDirty() ?? false); } isPointInPath(x, y) { this.updatePathIfDirty(); return this.path.closedPath && this.path.isPointInPath(x, y); } distanceSquared(x, y) { return this.distanceSquaredTransformedPoint(x, y); } svgPathData(transform) { this.updatePathIfDirty(); return this.path.toSVG(transform); } distanceSquaredTransformedPoint(x, y) { this.updatePathIfDirty(); if (this.path.closedPath && this.path.isPointInPath(x, y)) { return 0; } return this.path.distanceSquared(x, y); } isDirtyPath() { return false; } updatePath() { } updatePathIfDirty() { if (this.dirtyPath || this.isDirtyPath()) { this.updatePath(); this.dirtyPath = false; } } preRender(renderCtx) { if (renderCtx.devicePixelRatio !== this.lastPixelRatio) { this.dirtyPath = true; } this.lastPixelRatio = renderCtx.devicePixelRatio; this.updatePathIfDirty(); return super.preRender(renderCtx, this.path.commands.length); } render(renderCtx) { const { ctx } = renderCtx; if (this.clip && !isNaN(this._clipX) && !isNaN(this._clipY)) { ctx.save(); const margin = this.strokeWidth / 2; this._clipPath ?? (this._clipPath = new ExtendedPath2D()); this._clipPath.clear(); this._clipPath.rect(-margin, -margin, this._clipX + margin, this._clipY + margin + margin); ctx.clip(this._clipPath?.getPath2D()); if (this._clipX > 0 && this._clipY > 0) { this.drawPath(ctx); } ctx.restore(); } else { this._clipPath = void 0; this.drawPath(ctx); } this.fillShadow?.markClean(); super.render(renderCtx); } drawPath(ctx) { this.fillStroke(ctx, this.path.getPath2D()); } toSVG() { if (!this.visible) return; const element = createSvgElement("path"); element.setAttribute("d", this.svgPathData()); this.applySvgFillAttributes(element); this.applySvgStrokeAttributes(element); return { elements: [element] }; } }; Path.className = "Path"; __decorateClass([ ScenePathChangeDetection() ], Path.prototype, "clip", 2); __decorateClass([ ScenePathChangeDetection() ], Path.prototype, "clipX", 1); __decorateClass([ ScenePathChangeDetection() ], Path.prototype, "clipY", 1); // packages/ag-charts-community/src/chart/marker/shapes.ts function drawMarkerUnitPolygon(params, moves) { const { path, size } = params; const { x: x0, y: y0 } = params; path.clear(); let didMove = false; for (const [dx, dy] of moves) { const x = x0 + (dx - 0.5) * size; const y = y0 + (dy - 0.5) * size; if (didMove) { path.lineTo(x, y); } else { path.moveTo(x, y); } didMove = true; } path.closePath(); } var MARKER_SHAPES = { circle({ path, x, y, size }) { const r = size / 2; path.clear(); path.arc(x, y, r, 0, Math.PI * 2); path.closePath(); }, cross(params) { drawMarkerUnitPolygon(params, [ [0.25, 0], [0.5, 0.25], [0.75, 0], [1, 0.25], [0.75, 0.5], [1, 0.75], [0.75, 1], [0.5, 0.75], [0.25, 1], [0, 0.75], [0.25, 0.5], [0, 0.25] ]); }, diamond(params) { drawMarkerUnitPolygon(params, [ [0.5, 0], [1, 0.5], [0.5, 1], [0, 0.5] ]); }, heart({ path, x, y, size }) { const r = size / 4; y = y + r / 2; path.clear(); path.arc(x - r, y - r, r, toRadians(130), toRadians(330)); path.arc(x + r, y - r, r, toRadians(220), toRadians(50)); path.lineTo(x, y + r); path.closePath(); }, pin({ path, x, y, size: s }) { const cx = 0.5; const cy = 0.5; path.moveTo(x + (0.15625 - cx) * s, y + (0.34375 - cy) * s); path.cubicCurveTo( x + (0.15625 - cx) * s, y + (0.151491 - cy) * s, x + (0.307741 - cx) * s, y + (0 - cy) * s, x + (0.5 - cx) * s, y + (0 - cy) * s ); path.cubicCurveTo( x + (0.692259 - cx) * s, y + (0 - cy) * s, x + (0.84375 - cx) * s, y + (0.151491 - cy) * s, x + (0.84375 - cx) * s, y + (0.34375 - cy) * s ); path.cubicCurveTo( x + (0.84375 - cx) * s, y + (0.493824 - cy) * s, x + (0.784625 - cx) * s, y + (0.600181 - cy) * s, x + (0.716461 - cx) * s, y + (0.695393 - cy) * s ); path.cubicCurveTo( x + (0.699009 - cx) * s, y + (0.719769 - cy) * s, x + (0.681271 - cx) * s, y + (0.743104 - cy) * s, x + (0.663785 - cx) * s, y + (0.766105 - cy) * s ); path.cubicCurveTo( x + (0.611893 - cx) * s, y + (0.834367 - cy) * s, x + (0.562228 - cx) * s, y + (0.899699 - cy) * s, x + (0.528896 - cx) * s, y + (0.980648 - cy) * s ); path.cubicCurveTo( x + (0.524075 - cx) * s, y + (0.992358 - cy) * s, x + (0.512663 - cx) * s, y + (1 - cy) * s, x + (0.5 - cx) * s, y + (1 - cy) * s ); path.cubicCurveTo( x + (0.487337 - cx) * s, y + (1 - cy) * s, x + (0.475925 - cx) * s, y + (0.992358 - cy) * s, x + (0.471104 - cx) * s, y + (0.980648 - cy) * s ); path.cubicCurveTo( x + (0.487337 - cx) * s, y + (1 - cy) * s, x + (0.475925 - cx) * s, y + (0.992358 - cy) * s, x + (0.471104 - cx) * s, y + (0.980648 - cy) * s ); path.cubicCurveTo( x + (0.437772 - cx) * s, y + (0.899699 - cy) * s, x + (0.388107 - cx) * s, y + (0.834367 - cy) * s, x + (0.336215 - cx) * s, y + (0.766105 - cy) * s ); path.cubicCurveTo( x + (0.318729 - cx) * s, y + (0.743104 - cy) * s, x + (0.300991 - cx) * s, y + (0.719769 - cy) * s, x + (0.283539 - cx) * s, y + (0.695393 - cy) * s ); path.cubicCurveTo( x + (0.215375 - cx) * s, y + (0.600181 - cy) * s, x + (0.15625 - cx) * s, y + (0.493824 - cy) * s, x + (0.15625 - cx) * s, y + (0.34375 - cy) * s ); path.closePath(); }, plus(params) { drawMarkerUnitPolygon(params, [ [1 / 3, 0], [2 / 3, 0], [2 / 3, 1 / 3], [1, 1 / 3], [1, 2 / 3], [2 / 3, 2 / 3], [2 / 3, 1], [1 / 3, 1], [1 / 3, 2 / 3], [0, 2 / 3], [0, 1 / 3], [1 / 3, 1 / 3] ]); }, square({ path, x, y, size, pixelRatio }) { const hs = size / 2; path.clear(); path.moveTo(align(pixelRatio, x - hs), align(pixelRatio, y - hs)); path.lineTo(align(pixelRatio, x + hs), align(pixelRatio, y - hs)); path.lineTo(align(pixelRatio, x + hs), align(pixelRatio, y + hs)); path.lineTo(align(pixelRatio, x - hs), align(pixelRatio, y + hs)); path.closePath(); }, star({ path, x, y, size }) { const spikes = 5; const outerRadius = size / 2; const innerRadius = outerRadius / 2; const rotation = Math.PI / 2; for (let i = 0; i < spikes * 2; i++) { const radius = i % 2 === 0 ? outerRadius : innerRadius; const angle = i * Math.PI / spikes - rotation; const xCoordinate = x + Math.cos(angle) * radius; const yCoordinate = y + Math.sin(angle) * radius; path.lineTo(xCoordinate, yCoordinate); } path.closePath(); }, triangle(params) { drawMarkerUnitPolygon(params, [ [0.5, 0], [1, 0.87], [0, 0.87] ]); } }; // packages/ag-charts-community/src/chart/marker/marker.ts var InternalMarker = class extends Path { constructor() { super(...arguments); this.shape = "square"; this.x = 0; this.y = 0; this.size = 12; } updatePath() { const { path, shape, x, y, size } = this; const pixelRatio = this.layerManager?.canvas?.pixelRatio ?? 1; const anchor = Marker.anchor(shape); const drawParams = { path, x: x - (anchor.x - 0.5) * size, y: y - (anchor.y - 0.5) * size, size, pixelRatio }; path.clear(); if (typeof shape === "string") { MARKER_SHAPES[shape](drawParams); } else if (typeof shape === "function") { shape(drawParams); } } computeBBox() { const { x, y, size } = this; const anchor = Marker.anchor(this.shape); return new BBox(x - size * anchor.x, y - size * anchor.y, size, size); } executeFill(ctx, path) { if (!path) return; return super.executeFill(ctx, path); } executeStroke(ctx, path) { if (!path) return; return super.executeStroke(ctx, path); } }; __decorateClass([ ScenePathChangeDetection() ], InternalMarker.prototype, "shape", 2); __decorateClass([ ScenePathChangeDetection() ], InternalMarker.prototype, "x", 2); __decorateClass([ ScenePathChangeDetection() ], InternalMarker.prototype, "y", 2); __decorateClass([ ScenePathChangeDetection({ convertor: Math.abs }) ], InternalMarker.prototype, "size", 2); var Marker = class extends Rotatable(Scalable(Translatable(InternalMarker))) { static anchor(shape) { if (shape === "pin") { return { x: 0.5, y: 1 }; } else if (typeof shape === "function" && "anchor" in shape) { return shape.anchor; } return { x: 0.5, y: 0.5 }; } constructor(options) { super(options); if (options?.shape != null) { this.shape = options.shape; } } }; // packages/ag-charts-community/src/util/time/interval.ts var TimeInterval = class { constructor(_encode, _decode, _rangeCallback) { this._encode = _encode; this._decode = _decode; this._rangeCallback = _rangeCallback; } /** * Returns a new date representing the latest interval boundary date before or equal to date. * For example, `day.floor(date)` typically returns 12:00 AM local time on the given date. * @param date */ floor(date) { const d = new Date(date); const e = this._encode(d); return this._decode(e); } /** * Returns a new date representing the earliest interval boundary date after or equal to date. * @param date */ ceil(date) { const d = new Date(Number(date) - 1); const e = this._encode(d); return this._decode(e + 1); } /** * Returns an array of dates representing every interval boundary after or equal to start (inclusive) and before stop (exclusive). * @param start Range start. * @param stop Range end. * @param extend If specified, the requested range will be extended to the closest "nice" values. */ range(start, stop, { extend = false, visibleRange = [0, 1] } = {}) { let reversed = false; if (start.getTime() > stop.getTime()) { [start, stop] = [stop, start]; reversed = true; } const rangeCallback = this._rangeCallback?.(start, stop); const e0 = this._encode(extend ? this.floor(start) : this.ceil(start)); const e1 = this._encode(extend ? this.ceil(stop) : this.floor(stop)); if (e1 < e0) { return []; } const de = e1 - e0; let startIndex; let endIndex; if (reversed) { startIndex = Math.ceil(e0 + (1 - visibleRange[1]) * de); endIndex = Math.floor(e0 + (1 - visibleRange[0]) * de); } else { startIndex = Math.floor(e0 + visibleRange[0] * de); endIndex = Math.ceil(e0 + visibleRange[1] * de); } const range2 = []; for (let e = startIndex; e <= endIndex; e += 1) { const d = this._decode(e); range2.push(d); } rangeCallback?.(); return range2; } }; var CountableTimeInterval = class extends TimeInterval { getOffset(snapTo, step) { return Math.floor(this._encode(new Date(snapTo))) % step; } /** * Returns a filtered view of this interval representing every step'th date. * It can be a number of minutes, hours, days etc. * Must be a positive integer. * @param step */ every(step, options) { let offset4 = 0; let rangeCallback; const unsafeStep = step; step = Math.max(1, Math.round(step)); if (unsafeStep !== step) { logger_exports.warnOnce(`interval step of [${unsafeStep}] rounded to [${step}].`); } const { snapTo = "start" } = options ?? {}; if (typeof snapTo === "string") { const initialOffset = offset4; rangeCallback = (start, stop) => { const s = snapTo === "start" ? start : stop; offset4 = this.getOffset(s, step); return () => offset4 = initialOffset; }; } else if (typeof snapTo === "number") { offset4 = this.getOffset(new Date(snapTo), step); } else if (snapTo instanceof Date) { offset4 = this.getOffset(snapTo, step); } const encode7 = (date) => Math.floor((this._encode(date) - offset4) / step); const decode7 = (encoded) => this._decode(encoded * step + offset4); return new TimeInterval(encode7, decode7, rangeCallback); } }; // packages/ag-charts-community/src/util/time/duration.ts var durationSecond = 1e3; var durationMinute = durationSecond * 60; var durationHour = durationMinute * 60; var durationDay = durationHour * 24; var durationWeek = durationDay * 7; var durationMonth = durationDay * 30; var durationYear = durationDay * 365; // packages/ag-charts-community/src/util/time/second.ts var offset = (/* @__PURE__ */ new Date()).getTimezoneOffset() * durationMinute; function encode(date) { return Math.floor((date.getTime() - offset) / durationSecond); } function decode(encoded) { return new Date(offset + encoded * durationSecond); } var second = new CountableTimeInterval(encode, decode); // packages/ag-charts-community/src/util/time/minute.ts var offset2 = (/* @__PURE__ */ new Date()).getTimezoneOffset() * durationMinute; function encode2(date) { return Math.floor((date.getTime() - offset2) / durationMinute); } function decode2(encoded) { return new Date(offset2 + encoded * durationMinute); } var minute = new CountableTimeInterval(encode2, decode2); // packages/ag-charts-community/src/util/time/hour.ts var offset3 = (/* @__PURE__ */ new Date()).getTimezoneOffset() * durationMinute; function encode3(date) { return Math.floor((date.getTime() - offset3) / durationHour); } function decode3(encoded) { return new Date(offset3 + encoded * durationHour); } var hour = new CountableTimeInterval(encode3, decode3); // packages/ag-charts-community/src/util/time/day.ts function encode4(date) { const tzOffsetMs = date.getTimezoneOffset() * durationMinute; return Math.floor((date.getTime() - tzOffsetMs) / durationDay); } function decode4(encoded) { const d = new Date(1970, 0, 1); d.setDate(d.getDate() + encoded); return d; } var day = new CountableTimeInterval(encode4, decode4); // packages/ag-charts-community/src/util/time/week.ts function weekday(weekStart) { const thursday2 = 4; const dayShift = (7 + weekStart - thursday2) % 7; function encode7(date) { const tzOffsetMs = date.getTimezoneOffset() * durationMinute; return Math.floor((date.getTime() - tzOffsetMs) / durationWeek - dayShift / 7); } function decode7(encoded) { const d = new Date(1970, 0, 1); d.setDate(d.getDate() + encoded * 7 + dayShift); return d; } return new CountableTimeInterval(encode7, decode7); } var sunday = weekday(0); var monday = weekday(1); var tuesday = weekday(2); var wednesday = weekday(3); var thursday = weekday(4); var friday = weekday(5); var saturday = weekday(6); // packages/ag-charts-community/src/util/time/month.ts function encode5(date) { return date.getFullYear() * 12 + date.getMonth(); } function decode5(encoded) { const year2 = Math.floor(encoded / 12); const month2 = encoded - year2 * 12; return new Date(year2, month2, 1); } var month = new CountableTimeInterval(encode5, decode5); // packages/ag-charts-community/src/util/time/year.ts function encode6(date) { return date.getFullYear(); } function decode6(encoded) { const d = /* @__PURE__ */ new Date(); d.setFullYear(encoded); d.setMonth(0, 1); d.setHours(0, 0, 0, 0); return d; } var year = new CountableTimeInterval(encode6, decode6); // packages/ag-charts-community/src/util/timeFormatDefaults.ts function dateToNumber(value) { return value instanceof Date ? value.getTime() : value; } // packages/ag-charts-community/src/scale/scaleUtil.ts function filterVisibleTicks(ticks, reversed, visibleRange) { if (visibleRange == null || visibleRange[0] === 0 && visibleRange[1] === 1) return ticks; const vt0 = clamp(0, Math.floor(visibleRange[0] * ticks.length), ticks.length); const vt1 = clamp(0, Math.ceil(visibleRange[1] * ticks.length), ticks.length); const t0 = reversed ? ticks.length - vt1 : vt0; const t1 = reversed ? ticks.length - vt0 : vt1; return ticks.slice(t0, t1); } // packages/ag-charts-community/src/scale/bandScale.ts var _BandScale = class _BandScale extends AbstractScale { constructor() { super(...arguments); this.invalid = true; this.range = [0, 1]; this.round = false; this.interval = void 0; this._bandwidth = 1; this._step = 1; this._inset = 1; this._rawBandwidth = 1; /** * The ratio of the range that is reserved for space between bands. */ this._paddingInner = 0; /** * The ratio of the range that is reserved for space before the first * and after the last band. */ this._paddingOuter = 0; } static is(value) { return value instanceof _BandScale; } get bandwidth() { this.refresh(); return this._bandwidth; } get step() { this.refresh(); return this._step; } get inset() { this.refresh(); return this._inset; } get rawBandwidth() { this.refresh(); return this._rawBandwidth; } set padding(value) { value = clamp(0, value, 1); this._paddingInner = value; this._paddingOuter = value; } get padding() { return this._paddingInner; } set paddingInner(value) { this.invalid = true; this._paddingInner = clamp(0, value, 1); } get paddingInner() { return this._paddingInner; } set paddingOuter(value) { this.invalid = true; this._paddingOuter = clamp(0, value, 1); } get paddingOuter() { return this._paddingOuter; } refresh() { if (!this.invalid) return; this.invalid = false; this.update(); if (this.invalid) { logger_exports.warnOnce("Expected update to not invalidate scale"); } } ticks(_params, domain = this.domain, visibleRange) { return filterVisibleTicks(domain, false, visibleRange); } convert(d, _clamp) { this.refresh(); const i = this.getIndex(d); if (i == null || i < 0 || i >= this.domain.length) { return NaN; } return this.ordinalRange(i); } invertNearestIndex(position) { this.refresh(); const { domain } = this; if (domain.length === 0) return -1; let low = 0; let high = domain.length - 1; let closestDistance = Infinity; let closestIndex = 0; while (low <= high) { const mid = (high + low) / 2 | 0; const p = this.ordinalRange(mid); const distance = Math.abs(p - position); if (distance === 0) return mid; if (distance < closestDistance) { closestDistance = distance; closestIndex = mid; } if (p < position) { low = mid + 1; } else { high = mid - 1; } } return closestIndex; } update() { const count = this.domain.length; if (count === 0) return; const [r0, r1] = this.range; let { _paddingInner: paddingInner } = this; const { _paddingOuter: paddingOuter, round } = this; const rangeDistance = r1 - r0; let rawStep; if (count === 1) { paddingInner = 0; rawStep = rangeDistance * (1 - paddingOuter * 2); } else { rawStep = rangeDistance / Math.max(1, count - paddingInner + paddingOuter * 2); } const step = round ? Math.floor(rawStep) : rawStep; let inset = r0 + (rangeDistance - step * (count - paddingInner)) / 2; let bandwidth = step * (1 - paddingInner); if (round) { inset = Math.round(inset); bandwidth = Math.round(bandwidth); } this._step = step; this._inset = inset; this._bandwidth = bandwidth; this._rawBandwidth = rawStep * (1 - paddingInner); } ordinalRange(i) { const { _inset: inset, _step: step, range: range2 } = this; const min = Math.min(range2[0], range2[1]); const max = Math.max(range2[0], range2[1]); return clamp(min, inset + step * i, max); } }; __decorateClass([ Invalidating ], _BandScale.prototype, "range", 2); __decorateClass([ Invalidating ], _BandScale.prototype, "round", 2); __decorateClass([ Invalidating ], _BandScale.prototype, "interval", 2); var BandScale = _BandScale; // packages/ag-charts-community/src/scale/categoryScale.ts var CategoryScale = class extends BandScale { constructor() { super(...arguments); this.type = "band"; /** * Maps datum to its index in the {@link domain} array. * Used to check for duplicate data (not allowed). */ this.index = void 0; /** * Contains unique data only. */ this._domain = []; } set domain(values) { if (this._domain === values) return; this.invalid = true; this._domain = values; this.index = void 0; } get domain() { return this._domain; } normalizeDomains(...domains) { let normalizedDomain = void 0; const seenDomains = /* @__PURE__ */ new Set(); let animatable = true; for (const domain of domains) { if (seenDomains.has(domain)) continue; seenDomains.add(domain); if (normalizedDomain == null) { normalizedDomain = deduplicateCategories(domain); } else { animatable && (animatable = domainOrderedToNormalizedDomain(domain, normalizedDomain)); normalizedDomain = deduplicateCategories([...normalizedDomain, ...domain]); } } normalizedDomain ?? (normalizedDomain = []); return { domain: normalizedDomain, animatable }; } toDomain(_value) { return void 0; } invert(position, nearest = false) { this.refresh(); const offset4 = nearest ? this.bandwidth / 2 : 0; const index = this.invertNearestIndex(Math.max(0, position - offset4)); const matches = nearest || position === this.ordinalRange(index); return matches ? this.domain[index] : void 0; } getIndex(value) { let { index } = this; if (index == null) { const { domain } = this; index = /* @__PURE__ */ new Map(); for (let i = 0; i < domain.length; i++) { index.set(dateToNumber(domain[i]), i); } this.index = index; } return index.get(dateToNumber(value)); } }; function deduplicateCategories(d) { let domain; const uniqueValues = /* @__PURE__ */ new Set(); for (const value of d) { const key = dateToNumber(value); const lastSize = uniqueValues.size; uniqueValues.add(key); const isUniqueValue = uniqueValues.size !== lastSize; if (isUniqueValue) { domain?.push(value); } else { domain ?? (domain = d.slice(0, uniqueValues.size)); } } return domain ?? d; } function domainOrderedToNormalizedDomain(domain, normalizedDomain) { let normalizedIndex = -1; for (const value of domain) { const normalizedNextIndex = normalizedDomain.indexOf(value); if (normalizedNextIndex === -1) { normalizedIndex = Infinity; } else if (normalizedNextIndex <= normalizedIndex) { return false; } else { normalizedIndex = normalizedNextIndex; } } return true; } // packages/ag-charts-community/src/util/format.util.ts var defaultNumberFormatter = new Intl.NumberFormat("en-US", { maximumFractionDigits: 2, useGrouping: false }); var percentFormatter = new Intl.NumberFormat("en-US", { style: "percent" }); function formatValue(value, maximumFractionDigits = 2) { if (typeof value === "number") { return formatNumber(value, maximumFractionDigits); } return String(value ?? ""); } function formatNumber(value, maximumFractionDigits) { if (maximumFractionDigits === 2) { return defaultNumberFormatter.format(value); } return new Intl.NumberFormat("en-US", { maximumFractionDigits, useGrouping: false }).format(value); } // packages/ag-charts-community/src/util/numberFormat.ts function parseFormat(format) { let prefix; let suffix; const surrounded = surroundedRegEx.exec(format); if (surrounded) { [, prefix, format, suffix] = surrounded; } const match = formatRegEx.exec(format); if (!match) { throw new Error(`The number formatter is invalid: ${format}`); } const [, fill, align2, sign, symbol, zero, width, comma, precision, trim, type] = match; return { fill, align: align2, sign, symbol, zero, width: parseInt(width), comma, precision: parseInt(precision), trim: Boolean(trim), type, prefix, suffix }; } function numberFormat(format) { const options = typeof format === "string" ? parseFormat(format) : format; const { fill, align: align2, sign = "-", symbol, zero, width, comma, type, prefix = "", suffix = "", precision } = options; let { trim } = options; const precisionIsNaN = precision == null || isNaN(precision); let formatBody; if (!type) { formatBody = decimalTypes["g"]; trim = true; } else if (type in decimalTypes && type in integerTypes) { formatBody = precisionIsNaN ? integerTypes[type] : decimalTypes[type]; } else if (type in decimalTypes) { formatBody = decimalTypes[type]; } else if (type in integerTypes) { formatBody = integerTypes[type]; } else { throw new Error(`The number formatter type is invalid: ${type}`); } let formatterPrecision; if (precision == null || precisionIsNaN) { formatterPrecision = type ? 6 : 12; } else { formatterPrecision = precision; } return (n) => { let result = formatBody(n, formatterPrecision); if (trim) { result = removeTrailingZeros(result); } if (comma) { result = insertSeparator(result, comma); } result = addSign(n, result, sign); if (symbol && symbol !== "#") { result = `${symbol}${result}`; } if (symbol === "#" && type === "x") { result = `0x${result}`; } if (type === "s") { result = `${result}${getSIPrefix(n)}`; } if (type === "%" || type === "p") { result = `${result}%`; } if (width != null && !isNaN(width)) { result = addPadding(result, width, fill ?? zero, align2); } result = `${prefix}${result}${suffix}`; return result; }; } var formatRegEx = /^(?:(.)?([<>=^]))?([+\-( ])?([$€£¥₣₹#])?(0)?(\d+)?(,)?(?:\.(\d+))?(~)?([%a-z])?$/i; var surroundedRegEx = /^((?:[^#]|#[^{])*)#{([^}]+)}(.*)$/; var integerTypes = { b: (n) => absFloor(n).toString(2), c: (n) => String.fromCharCode(n), d: (n) => Math.round(Math.abs(n)).toFixed(0), o: (n) => absFloor(n).toString(8), x: (n) => absFloor(n).toString(16), X: (n) => integerTypes.x(n).toUpperCase(), n: (n) => integerTypes.d(n), "%": (n) => `${absFloor(n * 100).toFixed(0)}` }; var decimalTypes = { e: (n, f) => Math.abs(n).toExponential(f), E: (n, f) => decimalTypes.e(n, f).toUpperCase(), f: (n, f) => Math.abs(n).toFixed(f), F: (n, f) => decimalTypes.f(n, f).toUpperCase(), g: (n, f) => { if (n === 0) { return "0"; } const a = Math.abs(n); const p = Math.floor(Math.log10(a)); if (p >= -4 && p < f) { return a.toFixed(f - 1 - p); } return a.toExponential(f - 1); }, G: (n, f) => decimalTypes.g(n, f).toUpperCase(), n: (n, f) => decimalTypes.g(n, f), p: (n, f) => decimalTypes.r(n * 100, f), r: (n, f) => { if (n === 0) { return "0"; } const a = Math.abs(n); const p = Math.floor(Math.log10(a)); const q = p - (f - 1); if (q <= 0) { return a.toFixed(-q); } const x = 10 ** q; return (Math.round(a / x) * x).toFixed(); }, s: (n, f) => { const p = getSIPrefixPower(n); return decimalTypes.r(n / 10 ** p, f); }, "%": (n, f) => decimalTypes.f(n * 100, f) }; var minSIPrefix = -24; var maxSIPrefix = 24; var siPrefixes = { [minSIPrefix]: "y", [-21]: "z", [-18]: "a", [-15]: "f", [-12]: "p", [-9]: "n", [-6]: "\xB5", [-3]: "m", [0]: "", [3]: "k", [6]: "M", [9]: "G", [12]: "T", [15]: "P", [18]: "E", [21]: "Z", [maxSIPrefix]: "Y" }; var minusSign = "\u2212"; function absFloor(n) { return Math.floor(Math.abs(n)); } function removeTrailingZeros(numString) { return numString.replace(/\.0+$/, "").replace(/(\.[1-9])0+$/, "$1"); } function insertSeparator(numString, separator) { let dotIndex = numString.indexOf("."); if (dotIndex < 0) { dotIndex = numString.length; } const integerChars = numString.substring(0, dotIndex).split(""); const fractionalPart = numString.substring(dotIndex); for (let i = integerChars.length - 3; i > 0; i -= 3) { integerChars.splice(i, 0, separator); } return `${integerChars.join("")}${fractionalPart}`; } function getSIPrefix(n) { return siPrefixes[getSIPrefixPower(n)]; } function getSIPrefixPower(n) { return clamp(minSIPrefix, n ? Math.floor(Math.log10(Math.abs(n)) / 3) * 3 : 0, maxSIPrefix); } function addSign(num, numString, signType = "") { if (signType === "(") { return num >= 0 ? numString : `(${numString})`; } const plusSign = signType === "+" ? "+" : ""; return `${num >= 0 ? plusSign : minusSign}${numString}`; } function addPadding(numString, width, fill = " ", align2 = ">") { let result = numString; if (align2 === ">" || !align2) { result = result.padStart(width, fill); } else if (align2 === "<") { result = result.padEnd(width, fill); } else if (align2 === "^") { const padWidth = Math.max(0, width - result.length); const padLeft = Math.ceil(padWidth / 2); const padRight = Math.floor(padWidth / 2); result = result.padStart(padLeft + result.length, fill); result = result.padEnd(padRight + result.length, fill); } return result; } // packages/ag-charts-community/src/util/ticks.ts var tInterval = (timeInterval, baseDuration, step) => ({ duration: baseDuration * step, timeInterval, step }); var TickIntervals = [ tInterval(second, durationSecond, 1), tInterval(second, durationSecond, 5), tInterval(second, durationSecond, 15), tInterval(second, durationSecond, 30), tInterval(minute, durationMinute, 1), tInterval(minute, durationMinute, 5), tInterval(minute, durationMinute, 15), tInterval(minute, durationMinute, 30), tInterval(hour, durationHour, 1), tInterval(hour, durationHour, 3), tInterval(hour, durationHour, 6), tInterval(hour, durationHour, 12), tInterval(day, durationDay, 1), tInterval(day, durationDay, 2), tInterval(sunday, durationWeek, 1), tInterval(sunday, durationWeek, 2), tInterval(sunday, durationWeek, 3), tInterval(month, durationMonth, 1), tInterval(month, durationMonth, 2), tInterval(month, durationMonth, 3), tInterval(month, durationMonth, 4), tInterval(month, durationMonth, 6), tInterval(year, durationYear, 1) ]; var TickMultipliers = [1, 2, 5, 10]; function isCloseToInteger(n, delta3) { return Math.abs(Math.round(n) - n) < delta3; } function createTicks(start, stop, count, minCount, maxCount, visibleRange) { if (count < 2) { return [start, stop]; } const step = tickStep(start, stop, count, minCount, maxCount); if (!Number.isFinite(step)) { return []; } if (!isCloseToInteger(start / step, 1e-12)) { start = Math.ceil(start / step) * step; } if (!isCloseToInteger(stop / step, 1e-12)) { stop = Math.floor(stop / step) * step; } if (visibleRange != null && visibleRange[0] !== 0 && visibleRange[1] !== 1) { const rangeExtent = stop - start; const adjustedStart = start + rangeExtent * visibleRange[0]; const adjustedEnd = stop - rangeExtent * (1 - visibleRange[1]); return range(adjustedStart - adjustedStart % step, adjustedEnd + adjustedEnd % step, step); } return range(start, stop, step); } function tickStep(start, end, count, minCount = 0, maxCount = Infinity) { if (start === end) { return clamp(1, minCount, maxCount); } else if (count < 1) { return NaN; } const extent = Math.abs(end - start); const step = 10 ** Math.floor(Math.log10(extent / count)); let m = NaN, minDiff = Infinity, isInBounds = false; for (const multiplier of TickMultipliers) { const c = Math.ceil(extent / (multiplier * step)); const validBounds = c >= minCount && c <= maxCount; if (isInBounds && !validBounds) continue; const diffCount = Math.abs(c - count); if (minDiff > diffCount || isInBounds !== validBounds) { isInBounds || (isInBounds = validBounds); minDiff = diffCount; m = multiplier; } } return m * step; } function decimalPlaces(decimal) { for (let i = decimal.length - 1; i >= 0; i -= 1) { if (decimal[i] !== "0") { return i + 1; } } return 0; } function tickFormat(ticks, format) { const options = parseFormat(format ?? ",f"); if (options.precision == null || isNaN(options.precision)) { if (!options.type || "eEFgGnprs".includes(options.type)) { options.precision = Math.max( ...ticks.map((x) => { if (!Number.isFinite(x)) return 0; const [integer, decimal] = x.toExponential((options.type ? 6 : 12) - 1).split(/[.e]/g); return (integer !== "1" && integer !== "-1" ? 1 : 0) + decimalPlaces(decimal) + 1; }) ); } else if ("f%".includes(options.type)) { options.precision = Math.max( ...ticks.map((x) => { if (!Number.isFinite(x) || x === 0) return 0; const l = Math.floor(Math.log10(Math.abs(x))); const digits = options.type ? 6 : 12; const decimal = x.toExponential(digits - 1).split(/[.e]/g)[1]; const decimalLength = decimalPlaces(decimal); return Math.max(0, decimalLength - l); }) ); } } const formatter = numberFormat(options); return (n) => formatter(Number(n)); } function range(start, end, step) { if (!Number.isFinite(step) || step <= 0) { return []; } const f = 10 ** countFractionDigits(step); const d0 = Math.min(start, end); const d1 = Math.max(start, end); const out = []; for (let i = 0; ; i += 1) { const p = Math.round((d0 + step * i) * f) / f; if (p > d1) break; out.push(p); } return out; } function isDenseInterval(count, availableRange) { if (count >= availableRange) { logger_exports.warnOnce( `the configured interval results in more than 1 item per pixel, ignoring. Supply a larger interval or omit this configuration` ); return true; } return false; } function niceTicksDomain(start, end) { const extent = Math.abs(end - start); const step = 10 ** Math.floor(Math.log10(extent)); let minError = Infinity, ticks = [start, end]; for (const multiplier of TickMultipliers) { const m = multiplier * step; const d0 = Math.floor(start / m) * m; const d1 = Math.ceil(end / m) * m; const error2 = 1 - extent / Math.abs(d1 - d0); if (minError > error2) { minError = error2; ticks = [d0, d1]; } } return ticks; } // packages/ag-charts-community/src/scale/continuousScale.ts var _ContinuousScale = class _ContinuousScale extends AbstractScale { constructor(domain = [], range2 = []) { super(); this.domain = domain; this.range = range2; this.defaultClamp = false; } static is(value) { return value instanceof _ContinuousScale; } normalizeDomains(...domains) { let min; let minValue = Infinity; let max; let maxValue = -Infinity; for (const domain of domains) { for (const d of domain) { const value = d.valueOf(); if (value < minValue) { minValue = value; min = d; } if (value > maxValue) { maxValue = value; max = d; } } } if (min != null && max != null) { const domain = [min, max]; return { domain, animatable: true }; } else { return { domain: [], animatable: false }; } } transform(x) { return x; } transformInvert(x) { return x; } calcBandwidth(smallestInterval = 1) { const { domain } = this; const rangeDistance = this.getPixelRange(); if (domain.length === 0) return rangeDistance; const intervals = Math.abs(domain[1].valueOf() - domain[0].valueOf()) / smallestInterval + 1; const maxBands = Math.floor(rangeDistance); const bands = Math.min(intervals, maxBands); return rangeDistance / Math.max(1, bands); } convert(value, clamp2 = this.defaultClamp) { const { domain } = this; if (!domain || domain.length < 2) { return NaN; } const d0 = Number(this.transform(domain[0])); const d1 = Number(this.transform(domain[1])); const x = Number(this.transform(value)); const { range: range2 } = this; const [r0, r1] = range2; if (clamp2) { const [start, stop] = findMinMax([d0, d1]); if (x < start) { return r0; } else if (x > stop) { return r1; } } if (d0 === d1) { return (r0 + r1) / 2; } else if (x === d0) { return r0; } else if (x === d1) { return r1; } return r0 + (x - d0) / (d1 - d0) * (r1 - r0); } invert(x, _nearest) { const domain = this.domain.map((d2) => this.transform(d2)); const [d0, d1] = domain; const { range: range2 } = this; const [r0, r1] = range2; let d; if (r0 === r1) { d = this.toDomain((Number(d0) + Number(d1)) / 2); } else { d = this.toDomain(Number(d0) + (x - r0) / (r1 - r0) * (Number(d1) - Number(d0))); } return this.transformInvert(d); } getPixelRange() { const [a, b] = this.range; return Math.abs(b - a); } }; _ContinuousScale.defaultTickCount = 5; var ContinuousScale = _ContinuousScale; // packages/ag-charts-community/src/scale/linearScale.ts var LinearScale = class _LinearScale extends ContinuousScale { constructor() { super([0, 1], [0, 1]); this.type = "number"; } static getTickStep(start, stop, ticks) { const { interval, tickCount = ContinuousScale.defaultTickCount, minTickCount, maxTickCount } = ticks; return interval ?? tickStep(start, stop, tickCount, minTickCount, maxTickCount); } toDomain(d) { return d; } ticks({ interval, tickCount = ContinuousScale.defaultTickCount, minTickCount, maxTickCount }, domain = this.domain, visibleRange) { if (!domain || domain.length < 2 || tickCount < 1 || !domain.every(isFinite)) { return []; } const [d0, d1] = domain; if (interval) { const step = Math.abs(interval); if (!isDenseInterval((d1 - d0) / step, this.getPixelRange())) { return range(d0, d1, step); } } return createTicks(d0, d1, tickCount, minTickCount, maxTickCount, visibleRange); } niceDomain(ticks, domain = this.domain) { if (domain.length < 2) return []; const { tickCount = ContinuousScale.defaultTickCount } = ticks; let [start, stop] = domain; if (tickCount === 1) { [start, stop] = niceTicksDomain(start, stop); } else if (tickCount > 1) { const roundStart = start > stop ? Math.ceil : Math.floor; const roundStop = start > stop ? Math.floor : Math.ceil; const maxAttempts = 4; for (let i = 0; i < maxAttempts; i++) { const prev0 = start; const prev1 = stop; const step = _LinearScale.getTickStep(start, stop, ticks); const [d0, d1] = domain; start = roundStart(d0 / step) * step; stop = roundStop(d1 / step) * step; if (start === prev0 && stop === prev1) break; } } return [start, stop]; } tickFormatter({ ticks: specifiedTicks, fractionDigits, specifier }) { return specifier != null ? tickFormat(specifiedTicks, specifier) : (x) => formatValue(x, fractionDigits); } datumFormatter({ ticks: specifiedTicks, fractionDigits, specifier }) { return specifier != null ? tickFormat(specifiedTicks, specifier) : (x) => formatValue(x, fractionDigits + 1); } }; // packages/ag-charts-community/src/util/debug.ts var LONG_TIME_PERIOD_THRESHOLD = 2e3; var timeOfLastLog = Date.now(); var logTimeGap = () => { const timeSinceLastLog = Date.now() - timeOfLastLog; if (timeSinceLastLog > LONG_TIME_PERIOD_THRESHOLD) { const prettyDuration = (Math.floor(timeSinceLastLog / 100) / 10).toFixed(1); logger_exports.log(`**** ${prettyDuration}s since last log message ****`); } timeOfLastLog = Date.now(); }; var Debug = { create(...debugSelectors) { const resultFn = (...logContent) => { if (Debug.check(...debugSelectors)) { if (typeof logContent[0] === "function") { logContent = toArray(logContent[0]()); } logTimeGap(); logger_exports.log(...logContent); } }; return Object.assign(resultFn, { check: () => Debug.check(...debugSelectors), group: (name, cb) => { if (Debug.check(...debugSelectors)) { return logger_exports.logGroup(name, cb); } return cb(); } }); }, check(...debugSelectors) { if (debugSelectors.length === 0) { debugSelectors.push(true); } const chartDebug = toArray(getWindow("agChartsDebug")); return chartDebug.some((selector) => debugSelectors.includes(selector)); } }; // packages/ag-charts-community/src/scene/canvas/canvasUtil.ts function clearContext({ context, pixelRatio, width, height }) { context.save(); context.resetTransform(); context.clearRect(0, 0, Math.ceil(width * pixelRatio), Math.ceil(height * pixelRatio)); context.restore(); } function debugContext(ctx) { if (Debug.check("canvas")) { const save = ctx.save.bind(ctx); const restore = ctx.restore.bind(ctx); let depth = 0; Object.assign(ctx, { save() { save(); depth++; }, restore() { if (depth === 0) { throw new Error("AG Charts - Unable to restore() past depth 0"); } restore(); depth--; }, verifyDepthZero() { if (depth !== 0) { throw new Error(`AG Charts - Save/restore depth is non-zero: ${depth}`); } } }); } } // packages/ag-charts-community/src/scene/canvas/hdpiOffscreenCanvas.ts function canvasDimensions(width, height, pixelRatio) { return [Math.round(width * pixelRatio), Math.round(height * pixelRatio)]; } var HdpiOffscreenCanvas = class { constructor(options) { const { width, height, pixelRatio, willReadFrequently = false } = options; this.width = width; this.height = height; this.pixelRatio = pixelRatio; const [canvasWidth, canvasHeight] = canvasDimensions(width, height, pixelRatio); this.canvas = new OffscreenCanvas(canvasWidth, canvasHeight); this.context = this.canvas.getContext("2d", { willReadFrequently }); this.context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0); debugContext(this.context); } drawImage(context, dx = 0, dy = 0) { return context.drawImage(this.canvas, dx, dy); } transferToImageBitmap() { return this.canvas.transferToImageBitmap(); } resize(width, height, pixelRatio) { if (!(width > 0 && height > 0)) return; const { canvas, context } = this; if (width !== this.width || height !== this.height || pixelRatio !== this.pixelRatio) { const [canvasWidth, canvasHeight] = canvasDimensions(width, height, pixelRatio); canvas.width = canvasWidth; canvas.height = canvasHeight; } context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0); this.width = width; this.height = height; this.pixelRatio = pixelRatio; } clear() { clearContext(this); } destroy() { this.canvas.width = 0; this.canvas.height = 0; this.context.clearRect(0, 0, 0, 0); this.canvas = null; this.context = null; Object.freeze(this); } }; // packages/ag-charts-community/src/scene/zIndex.ts var cmp = (a, b) => Math.sign(a - b); function compareZIndex(a, b) { if (typeof a === "number" && typeof b === "number") { return cmp(a, b); } const aArray = typeof a === "number" ? [a] : a; const bArray = typeof b === "number" ? [b] : b; const length = Math.min(aArray.length, bArray.length); for (let i = 0; i < length; i += 1) { const diff = cmp(aArray[i], bArray[i]); if (diff !== 0) return diff; } return cmp(aArray.length, bArray.length); } // packages/ag-charts-community/src/scene/group.ts var sharedOffscreenCanvas; var _Group = class _Group extends Node { // optimizeForInfrequentRedraws: true constructor(opts) { super(opts); this.opacity = 1; this.renderToOffscreenCanvas = false; this.optimizeForInfrequentRedraws = false; // Used when renderToOffscreenCanvas: true this.layer = void 0; // optimizeForInfrequentRedraws: false this.image = void 0; this._lastWidth = NaN; this._lastHeight = NaN; this._lastDevicePixelRatio = NaN; this.isContainerNode = true; this.renderToOffscreenCanvas = opts?.renderToOffscreenCanvas === true; } static is(value) { return value instanceof _Group; } static computeChildrenBBox(nodes, skipInvisible = true) { return BBox.merge(Node.extractBBoxes(nodes, skipInvisible)); } static compareChildren(a, b) { return compareZIndex(a.zIndex, b.zIndex) || a.serialNumber - b.serialNumber; } // We consider a group to be boundless, thus any point belongs to it. containsPoint(_x, _y) { return true; } computeBBox() { return _Group.computeChildrenBBox(this.children()); } computeSafeClippingBBox(pixelRatio) { const bbox = this.computeBBox(); if (!bbox.isFinite()) return; let strokeWidth = 0; const strokeMiterAmount = 4; for (const child of this.descendants()) { if (child instanceof Shape) { strokeWidth = Math.max(strokeWidth, child.strokeWidth); } } const padding = Math.max( // Account for anti-aliasing artefacts 1, // Account for strokes (incl. miters) - this may not be the best place to include this strokeWidth / 2 * strokeMiterAmount ); const { x: originX, y: originY } = Transformable.toCanvasPoint(this, 0, 0); const x = alignBefore(pixelRatio, originX + bbox.x - padding) - originX; const y = alignBefore(pixelRatio, originY + bbox.y - padding) - originY; const width = Math.ceil(bbox.x + bbox.width - x + padding); const height = Math.ceil(bbox.y + bbox.height - y + padding); return new BBox(x, y, width, height); } prepareSharedCanvas(width, height, pixelRatio) { if (sharedOffscreenCanvas == null || sharedOffscreenCanvas.pixelRatio !== pixelRatio) { sharedOffscreenCanvas = new HdpiOffscreenCanvas({ width, height, pixelRatio }); } else { sharedOffscreenCanvas.resize(width, height, pixelRatio); } return sharedOffscreenCanvas; } isDirty(renderCtx) { const { width, height, devicePixelRatio } = renderCtx; const { dirty, dirtyZIndex, layer } = this; const layerResized = layer != null && (this._lastWidth !== width || this._lastHeight !== height); const pixelRatioChanged = this._lastDevicePixelRatio !== devicePixelRatio; this._lastWidth = width; this._lastHeight = height; this._lastDevicePixelRatio = devicePixelRatio; if (dirty || dirtyZIndex || layerResized || pixelRatioChanged) return true; for (const child of this.children()) { if (child.dirty) return true; } return false; } preRender(renderCtx) { const counts = super.preRender(renderCtx, 0); counts.groups += 1; counts.nonGroups -= 1; if (this.renderToOffscreenCanvas && !this.optimizeForInfrequentRedraws && counts.nonGroups > 0 && this.getVisibility()) { this.layer ?? (this.layer = this._layerManager?.addLayer({ name: this.name })); } else if (this.layer != null) { this._layerManager?.removeLayer(this.layer); this.layer = void 0; } return counts; } render(renderCtx) { const { layer, renderToOffscreenCanvas } = this; const childRenderCtx = { ...renderCtx }; if (!renderToOffscreenCanvas) { this.renderInContext(childRenderCtx); super.render(childRenderCtx); return; } const { ctx, stats, devicePixelRatio: pixelRatio } = renderCtx; let { image } = this; if (this.isDirty(renderCtx)) { image?.bitmap.close(); image = void 0; const bbox = layer ? void 0 : this.computeSafeClippingBBox(pixelRatio); const renderOffscreen = (offscreenCanvas, ...transform) => { const offscreenCtx = offscreenCanvas.context; childRenderCtx.ctx = offscreenCtx; offscreenCanvas.clear(); offscreenCtx.save(); offscreenCtx.setTransform(...transform); offscreenCtx.globalAlpha = 1; this.renderInContext(childRenderCtx); offscreenCtx.restore(); offscreenCtx.verifyDepthZero?.(); }; if (layer) { renderOffscreen(layer, ctx.getTransform()); } else if (bbox) { const { x, y, width, height } = bbox; const canvas = this.prepareSharedCanvas(width, height, pixelRatio); renderOffscreen(canvas, pixelRatio, 0, 0, pixelRatio, -x * pixelRatio, -y * pixelRatio); image = { bitmap: canvas.transferToImageBitmap(), x, y, width, height }; } else if (this.dirtyZIndex) { this.sortChildren(_Group.compareChildren); } this.image = image; if (stats) stats.layersRendered++; } else { this.skipRender(childRenderCtx); if (stats) stats.layersSkipped++; } const { globalAlpha } = ctx; ctx.globalAlpha = globalAlpha * this.opacity; if (layer) { ctx.save(); ctx.resetTransform(); layer.drawImage(ctx); ctx.restore(); } else if (image) { const { bitmap, x, y, width, height } = image; ctx.drawImage(bitmap, 0, 0, width * pixelRatio, height * pixelRatio, x, y, width, height); } ctx.globalAlpha = globalAlpha; super.render(childRenderCtx); } skipRender(childRenderCtx) { const { stats } = childRenderCtx; for (const child of this.children()) { child.markClean(); if (stats) { stats.nodesSkipped += this.childNodeCounts.groups + this.childNodeCounts.nonGroups; stats.opsSkipped += this.childNodeCounts.complexity; } } } applyClip(ctx, clipRect) { const { x, y, width, height } = clipRect; ctx.beginPath(); ctx.rect(x, y, width, height); ctx.clip(); } renderInContext(childRenderCtx) { const { ctx, stats } = childRenderCtx; if (this.dirtyZIndex) { this.sortChildren(_Group.compareChildren); } ctx.save(); ctx.globalAlpha *= this.opacity; if (this.clipRect != null) { this.applyClip(ctx, this.clipRect); childRenderCtx.clipBBox = Transformable.toCanvas(this, this.clipRect); } for (const child of this.children()) { if (!child.visible) { child.markClean(); if (stats) { stats.nodesSkipped += child.childNodeCounts.nonGroups + child.childNodeCounts.groups; stats.opsSkipped += child.childNodeCounts.complexity; } continue; } ctx.save(); child.render(childRenderCtx); ctx.restore(); } ctx.restore(); } /** * Transforms bbox given in the canvas coordinate space to bbox in this group's coordinate space and * sets this group's clipRect to the transformed bbox. * @param bbox clipRect bbox in the canvas coordinate space. */ setClipRect(bbox) { this.clipRect = bbox ? Transformable.fromCanvas(this, bbox) : void 0; } /** * Set the clip rect within the canvas coordinate space. * @param bbox clipRect bbox in the canvas coordinate space. */ setClipRectCanvasSpace(bbox) { this.clipRect = bbox; } _setLayerManager(layersManager) { if (this.layer) { this._layerManager?.removeLayer(this.layer); this.layer = void 0; } super._setLayerManager(layersManager); } getVisibility() { for (const node of this.traverseUp(true)) { if (!node.visible) { return false; } } return true; } toSVG() { if (!this.visible) return; const defs = []; const elements = []; for (const child of this.children()) { const svg = child.toSVG(); if (svg != null) { elements.push(...svg.elements); if (svg.defs != null) { defs.push(...svg.defs); } } } return { elements, defs }; } }; _Group.className = "Group"; __decorateClass([ SceneChangeDetection({ convertor: (v) => clamp(0, v, 1) }) ], _Group.prototype, "opacity", 2); var Group = _Group; var ScalableGroup = class extends Scalable(Group) { }; var RotatableGroup = class extends Rotatable(Group) { }; var TranslatableGroup = class extends Translatable(Group) { }; var TransformableGroup = class extends Rotatable(Translatable(Group)) { }; // packages/ag-charts-community/src/scene/canvas/hdpiCanvas.ts var HdpiCanvas = class { constructor(options) { this.enabled = true; this.width = 600; this.height = 300; const { width, height, canvasElement, willReadFrequently = false } = options; this.pixelRatio = options.pixelRatio ?? getWindow("devicePixelRatio") ?? 1; this.element = canvasElement ?? createElement("canvas"); this.element.style.display = "block"; this.element.style.width = (width ?? this.width) + "px"; this.element.style.height = (height ?? this.height) + "px"; this.element.width = Math.round((width ?? this.width) * this.pixelRatio); this.element.height = Math.round((height ?? this.height) * this.pixelRatio); this.context = this.element.getContext("2d", { willReadFrequently }); this.onEnabledChange(); this.resize(width ?? 0, height ?? 0, this.pixelRatio); debugContext(this.context); } drawImage(context, dx = 0, dy = 0) { return context.drawImage(this.context.canvas, dx, dy); } toDataURL(type) { return this.element.toDataURL(type); } resize(width, height, pixelRatio) { if (!(width > 0 && height > 0)) return; const { element, context } = this; element.width = Math.round(width * pixelRatio); element.height = Math.round(height * pixelRatio); context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0); element.style.width = width + "px"; element.style.height = height + "px"; this.width = width; this.height = height; this.pixelRatio = pixelRatio; } clear() { clearContext(this); } destroy() { this.element.remove(); this.element.width = 0; this.element.height = 0; this.context.clearRect(0, 0, 0, 0); Object.freeze(this); } onEnabledChange() { if (this.element) { this.element.style.display = this.enabled ? "" : "none"; } } }; __decorateClass([ ObserveChanges((target) => target.onEnabledChange()) ], HdpiCanvas.prototype, "enabled", 2); // packages/ag-charts-community/src/scene/layersManager.ts var LayersManager = class { constructor(canvas) { this.canvas = canvas; this.debug = Debug.create(true, "scene"); this.layersMap = /* @__PURE__ */ new Map(); this.nextLayerId = 0; } get size() { return this.layersMap.size; } resize(width, height, pixelRatio) { this.canvas.resize(width, height, pixelRatio); this.layersMap.forEach(({ canvas }) => canvas.resize(width, height, pixelRatio)); } addLayer(opts) { const { width, height, pixelRatio } = this.canvas; const { name } = opts; const canvas = new HdpiOffscreenCanvas({ width, height, pixelRatio }); this.layersMap.set(canvas, { id: this.nextLayerId++, name, canvas }); this.debug("Scene.addLayer() - layers", this.layersMap); return canvas; } removeLayer(canvas) { if (this.layersMap.has(canvas)) { this.layersMap.delete(canvas); canvas.destroy(); this.debug("Scene.removeLayer() - layers", this.layersMap); } } clear() { for (const layer of this.layersMap.values()) { layer.canvas.destroy(); } this.layersMap.clear(); } }; // packages/ag-charts-community/src/scene/sceneDebug.ts function formatBytes(value) { for (const unit of ["B", "KB", "MB", "GB"]) { if (value < 1536) { return `${value.toFixed(1)}${unit}`; } value /= 1024; } return `${value.toFixed(1)}TB}`; } function memoryUsage() { if (!("memory" in performance)) return; const { totalJSHeapSize, usedJSHeapSize, jsHeapSizeLimit } = performance.memory; const result = []; for (const amount of [usedJSHeapSize, totalJSHeapSize, jsHeapSizeLimit]) { if (typeof amount !== "number") continue; result.push(formatBytes(amount)); } return `Heap ${result.join(" / ")}`; } function debugStats(layersManager, debugSplitTimes, ctx, renderCtxStats, extraDebugStats = {}, seriesRect = BBox.zero) { if (!Debug.check("scene:stats" /* SCENE_STATS */, "scene:stats:verbose" /* SCENE_STATS_VERBOSE */)) return; const { layersRendered = 0, layersSkipped = 0, nodesRendered = 0, nodesSkipped = 0, opsPerformed = 0, opsSkipped = 0 } = renderCtxStats ?? {}; const end = performance.now(); const { start, ...durations } = debugSplitTimes; const splits = Object.entries(durations).map(([n, t]) => { return time(n, t); }).filter((v) => v != null).join(" + "); const extras = Object.entries(extraDebugStats).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join(" ; "); const detailedStats = Debug.check("scene:stats:verbose" /* SCENE_STATS_VERBOSE */); const memUsage = memoryUsage(); const stats = [ `${time("\u23F1\uFE0F", start, end)} (${splits})`, `${extras}`, `Layers: ${detailedStats ? pct(layersRendered, layersSkipped) : layersManager.size}`, detailedStats ? `Nodes: ${pct(nodesRendered, nodesSkipped)}` : null, detailedStats ? `Ops: ${pct(opsPerformed, opsSkipped)}` : null, detailedStats && memUsage ? memUsage : null ].filter(isString); const measurer = new SimpleTextMeasurer((t) => ctx.measureText(t)); const statsSize = new Map(stats.map((t) => [t, measurer.measureLines(t)])); const width = Math.max(...Array.from(statsSize.values(), (s) => s.width)); const height = accumulate(statsSize.values(), (s) => s.height); const x = 2 + seriesRect.x; ctx.save(); ctx.fillStyle = "white"; ctx.fillRect(x, 0, width, height); ctx.fillStyle = "black"; let y = 0; for (const [stat, size] of statsSize.entries()) { y += size.height; ctx.fillText(stat, x, y); } ctx.restore(); } function prepareSceneNodeHighlight(ctx) { const config = toArray(getWindow("agChartsSceneDebug")); const result = []; for (const name of config) { if (name === "layout") { result.push("seriesRoot", "legend", "root", /.*Axis-\d+-axis.*/); } else { result.push(name); } } ctx.debugNodeSearch = result; } function debugSceneNodeHighlight(ctx, debugNodes) { ctx.save(); for (const [name, node] of Object.entries(debugNodes)) { const bbox = Transformable.toCanvas(node); if (!bbox) { logger_exports.log(`Scene.render() - no bbox for debugged node [${name}].`); continue; } ctx.globalAlpha = 0.8; ctx.strokeStyle = "red"; ctx.lineWidth = 1; ctx.strokeRect(bbox.x, bbox.y, bbox.width, bbox.height); ctx.fillStyle = "red"; ctx.strokeStyle = "white"; ctx.font = "16px sans-serif"; ctx.textBaseline = "top"; ctx.textAlign = "left"; ctx.lineWidth = 2; ctx.strokeText(name, bbox.x, bbox.y, bbox.width); ctx.fillText(name, bbox.x, bbox.y, bbox.width); } ctx.restore(); } var skippedProperties = /* @__PURE__ */ new Set(); var allowedProperties = /* @__PURE__ */ new Set([ "gradient", // '_datum', "zIndex", "clipRect", "cachedBBox", "childNodeCounts", "path", "__zIndex", "name", "__scalingCenterX", "__scalingCenterY", "__rotationCenterX", "__rotationCenterY", "_previousDatum", "__fill", "__lineDash", "borderPath", "borderClipPath", "_clipPath" ]); function nodeProps(node) { const { ...allProps } = node; for (const prop of Object.keys(allProps)) { if (allowedProperties.has(prop)) continue; if (typeof allProps[prop] === "number") continue; if (typeof allProps[prop] === "string") continue; if (typeof allProps[prop] === "boolean") continue; skippedProperties.add(prop); delete allProps[prop]; } return allProps; } function buildTree(node, mode) { if (!Debug.check(true, "scene" /* SCENE */)) { return {}; } let order = 0; return { node: mode === "json" ? nodeProps(node) : node, name: node.name ?? node.id, dirty: node.dirty, ...Array.from(node.children(), (c) => buildTree(c, mode)).reduce( (result, childTree) => { let { name: treeNodeName } = childTree; const { node: { visible, opacity, zIndex, translationX, translationY, rotation, scalingX, scalingY }, node: childNode } = childTree; if (!visible || opacity <= 0) { treeNodeName = `(${treeNodeName})`; } if (Group.is(childNode) && childNode.renderToOffscreenCanvas) { treeNodeName = `*${treeNodeName}*`; } const zIndexString = Array.isArray(zIndex) ? `(${zIndex.join(", ")})` : zIndex; const key = [ `${(order++).toString().padStart(3, "0")}|`, `${treeNodeName ?? ""}`, `z: ${zIndexString}`, translationX && `x: ${translationX}`, translationY && `y: ${translationY}`, rotation && `r: ${rotation}`, scalingX != null && scalingX !== 1 && `sx: ${scalingX}`, scalingY != null && scalingY !== 1 && `sy: ${scalingY}` ].filter((v) => !!v).join(" "); let selectedKey = key; let index = 1; while (result[selectedKey] != null && index < 100) { selectedKey = `${key} (${index++})`; } result[selectedKey] = childTree; return result; }, {} ) }; } function buildDirtyTree(node) { if (!node.dirty) { return { dirtyTree: {}, paths: [] }; } const childrenDirtyTree = Array.from(node.children(), (c) => buildDirtyTree(c)).filter((c) => c.paths.length > 0); const name = Group.is(node) ? node.name ?? node.id : node.id; const paths = childrenDirtyTree.length ? childrenDirtyTree.flatMap((c) => c.paths).map((p) => `${name}.${p}`) : [name]; return { dirtyTree: { name, node, dirty: node.dirty, ...childrenDirtyTree.map((c) => c.dirtyTree).filter((t) => t.dirty != null).reduce((result, childTree) => { result[childTree.name ?? ""] = childTree; return result; }, {}) }, paths }; } function pct(rendered, skipped) { const total = rendered + skipped; return `${rendered} / ${total} (${Math.round(100 * rendered / total)}%)`; } function time(name, start, end) { const duration = end != null ? end - start : start; return `${name}: ${Math.round(duration * 100) / 100}ms`; } function accumulate(iterator, mapper) { let sum = 0; for (const item of iterator) { sum += mapper(item); } return sum; } // packages/ag-charts-community/src/scene/scene.ts var Scene = class { constructor(canvasOptions) { this.debug = Debug.create(true, "scene" /* SCENE */); this.id = createId(this); this.root = null; this.pendingSize = null; this.isDirty = false; this.canvas = new HdpiCanvas(canvasOptions); this.layersManager = new LayersManager(this.canvas); } get width() { return this.pendingSize?.[0] ?? this.canvas.width; } get height() { return this.pendingSize?.[1] ?? this.canvas.height; } get pixelRatio() { return this.pendingSize?.[2] ?? this.canvas.pixelRatio; } /** @deprecated v10.2.0 Only used by AG Grid Sparklines */ setContainer(value) { const { element } = this.canvas; element.parentElement?.removeChild(element); value.appendChild(element); return this; } setRoot(node) { if (this.root === node) { return this; } this.isDirty = true; this.root?._setLayerManager(); this.root = node; if (node) { node.visible = true; node._setLayerManager(this.layersManager); } return this; } clear() { this.canvas.clear(); } attachNode(node) { this.appendChild(node); return () => this.removeChild(node); } appendChild(node) { this.root?.appendChild(node); return this; } removeChild(node) { this.root?.removeChild(node); return this; } download(fileName, fileFormat) { downloadUrl(this.canvas.toDataURL(fileFormat), fileName?.trim() ?? "image"); } /** NOTE: Integrated Charts undocumented image download method. */ getDataURL(fileFormat) { return this.canvas.toDataURL(fileFormat); } resize(width, height, pixelRatio) { width = Math.round(width); height = Math.round(height); pixelRatio ?? (pixelRatio = this.pixelRatio); if (width > 0 && height > 0 && (width !== this.width || height !== this.height || pixelRatio !== this.pixelRatio)) { this.pendingSize = [width, height, pixelRatio]; this.isDirty = true; return true; } return false; } render(opts) { const { debugSplitTimes = { start: performance.now() }, extraDebugStats, seriesRect } = opts ?? {}; const { canvas, canvas: { context: ctx } = {}, root, pendingSize, width, height, pixelRatio: devicePixelRatio } = this; if (!ctx) { return; } const renderStartTime = performance.now(); if (pendingSize) { this.layersManager.resize(...pendingSize); this.pendingSize = null; } if (root && !root.visible) { this.isDirty = false; return; } if (root?.dirty === false && !this.isDirty) { if (this.debug.check()) { this.debug("Scene.render() - no-op", { tree: buildTree(root, "console") }); } debugStats(this.layersManager, debugSplitTimes, ctx, void 0, extraDebugStats, seriesRect); return; } const renderCtx = { ctx, width, height, devicePixelRatio, debugNodes: {} }; if (Debug.check("scene:stats:verbose" /* SCENE_STATS_VERBOSE */)) { renderCtx.stats = { layersRendered: 0, layersSkipped: 0, nodesRendered: 0, nodesSkipped: 0, opsPerformed: 0, opsSkipped: 0 }; } prepareSceneNodeHighlight(renderCtx); let canvasCleared = false; if (root?.dirty !== false) { canvasCleared = true; canvas.clear(); } if (root && Debug.check("scene:dirtyTree" /* SCENE_DIRTY_TREE */)) { const { dirtyTree, paths } = buildDirtyTree(root); Debug.create("scene:dirtyTree" /* SCENE_DIRTY_TREE */)("Scene.render() - dirtyTree", { dirtyTree, paths }); } if (root && canvasCleared) { if (root.visible) { root.preRender(renderCtx); } if (this.debug.check()) { const tree = buildTree(root, "console"); this.debug("Scene.render() - before", { canvasCleared, tree }); } if (root.visible) { ctx.save(); root.render(renderCtx); ctx.restore(); } } debugSplitTimes["\u270D\uFE0F"] = performance.now() - renderStartTime; ctx.verifyDepthZero?.(); this.isDirty = false; debugStats(this.layersManager, debugSplitTimes, ctx, renderCtx.stats, extraDebugStats, seriesRect); debugSceneNodeHighlight(ctx, renderCtx.debugNodes); if (root && this.debug.check()) { this.debug("Scene.render() - after", { tree: buildTree(root, "console"), canvasCleared }); } } toSVG() { const { root, width, height } = this; if (root == null) return; return Node.toSVG(root, width, height); } /** Alternative to destroy() that preserves re-usable resources. */ strip() { const { context, pixelRatio } = this.canvas; context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0); this.layersManager.clear(); this.setRoot(null); this.isDirty = false; } destroy() { this.strip(); this.canvas.destroy(); Object.assign(this, { canvas: void 0 }); } }; Scene.className = "Scene"; // packages/ag-charts-community/src/scene/shape/arc.ts var Arc = class extends Path { constructor() { super(); this.centerX = 0; this.centerY = 0; this.radius = 10; this.startAngle = 0; this.endAngle = Math.PI * 2; this.counterClockwise = false; this.type = 0 /* Open */; this.restoreOwnStyles(); } get fullPie() { return isNumberEqual(normalizeAngle360(this.startAngle), normalizeAngle360(this.endAngle)); } updatePath() { const path = this.path; path.clear(); path.arc(this.centerX, this.centerY, this.radius, this.startAngle, this.endAngle, this.counterClockwise); if (this.type === 1 /* Chord */) { path.closePath(); } else if (this.type === 2 /* Round */ && !this.fullPie) { path.lineTo(this.centerX, this.centerY); path.closePath(); } } computeBBox() { return new BBox(this.centerX - this.radius, this.centerY - this.radius, this.radius * 2, this.radius * 2); } isPointInPath(x, y) { const bbox = this.getBBox(); return this.type !== 0 /* Open */ && bbox.containsPoint(x, y) && this.path.isPointInPath(x, y); } }; Arc.className = "Arc"; __decorateClass([ ScenePathChangeDetection() ], Arc.prototype, "centerX", 2); __decorateClass([ ScenePathChangeDetection() ], Arc.prototype, "centerY", 2); __decorateClass([ ScenePathChangeDetection() ], Arc.prototype, "radius", 2); __decorateClass([ ScenePathChangeDetection() ], Arc.prototype, "startAngle", 2); __decorateClass([ ScenePathChangeDetection() ], Arc.prototype, "endAngle", 2); __decorateClass([ ScenePathChangeDetection() ], Arc.prototype, "counterClockwise", 2); __decorateClass([ ScenePathChangeDetection() ], Arc.prototype, "type", 2); // packages/ag-charts-community/src/scene/shape/line.ts var Line = class extends Shape { constructor(opts = {}) { super(opts); this.x1 = 0; this.y1 = 0; this.x2 = 0; this.y2 = 0; this.restoreOwnStyles(); } set x(value) { this.x1 = value; this.x2 = value; } set y(value) { this.y1 = value; this.y2 = value; } get midPoint() { return { x: (this.x1 + this.x2) / 2, y: (this.y1 + this.y2) / 2 }; } computeBBox() { return new BBox( Math.min(this.x1, this.x2), Math.min(this.y1, this.y2), Math.abs(this.x2 - this.x1), Math.abs(this.y2 - this.y1) ); } isPointInPath(x, y) { if (this.x1 === this.x2 || this.y1 === this.y2) { return this.getBBox().clone().grow(this.strokeWidth / 2).containsPoint(x, y); } return false; } distanceSquared(px, py) { const { x1, y1, x2, y2 } = this; return lineDistanceSquared(px, py, x1, y1, x2, y2, Infinity); } render(renderCtx) { const { ctx, devicePixelRatio } = renderCtx; let { x1, y1, x2, y2 } = this; if (x1 === x2) { const { strokeWidth } = this; const x = Math.round(x1 * devicePixelRatio) / devicePixelRatio + Math.trunc(strokeWidth * devicePixelRatio) % 2 / (devicePixelRatio * 2); x1 = x; x2 = x; } else if (y1 === y2) { const { strokeWidth } = this; const y = Math.round(y1 * devicePixelRatio) / devicePixelRatio + Math.trunc(strokeWidth * devicePixelRatio) % 2 / (devicePixelRatio * 2); y1 = y; y2 = y; } ctx.beginPath(); ctx.moveTo(x1, y1); ctx.lineTo(x2, y2); this.fillStroke(ctx); this.fillShadow?.markClean(); super.render(renderCtx); } toSVG() { if (!this.visible) return; const element = createSvgElement("line"); element.setAttribute("x1", String(this.x1)); element.setAttribute("y1", String(this.y1)); element.setAttribute("x2", String(this.x2)); element.setAttribute("y2", String(this.y2)); this.applySvgStrokeAttributes(element); return { elements: [element] }; } }; Line.className = "Line"; Line.defaultStyles = { ...Shape.defaultStyles, fill: void 0, strokeWidth: 1 }; __decorateClass([ SceneChangeDetection() ], Line.prototype, "x1", 2); __decorateClass([ SceneChangeDetection() ], Line.prototype, "y1", 2); __decorateClass([ SceneChangeDetection() ], Line.prototype, "x2", 2); __decorateClass([ SceneChangeDetection() ], Line.prototype, "y2", 2); // packages/ag-charts-community/src/scene/shape/radialColumnShape.ts function rotatePoint(x, y, rotation) { const radius = Math.sqrt(x ** 2 + y ** 2); const angle = Math.atan2(y, x); const rotated = angle + rotation; return { x: Math.cos(rotated) * radius, y: Math.sin(rotated) * radius }; } var RadialColumnShape = class extends Path { constructor() { super(...arguments); this.isBeveled = true; this.columnWidth = 0; this.startAngle = 0; this.endAngle = 0; this.outerRadius = 0; this.innerRadius = 0; this.axisInnerRadius = 0; this.axisOuterRadius = 0; this.isRadiusAxisReversed = false; } set cornerRadius(_value) { } computeBBox() { const { innerRadius, outerRadius, columnWidth } = this; const rotation = this.getRotation(); const left = -columnWidth / 2; const right = columnWidth / 2; const top = -outerRadius; const bottom = -innerRadius; let x0 = Infinity; let y0 = Infinity; let x1 = -Infinity; let y1 = -Infinity; for (let i = 0; i < 4; i += 1) { const { x, y } = rotatePoint(i % 2 === 0 ? left : right, i < 2 ? top : bottom, rotation); x0 = Math.min(x, x0); y0 = Math.min(y, y0); x1 = Math.max(x, x1); y1 = Math.max(y, y1); } return new BBox(x0, y0, x1 - x0, y1 - y0); } getRotation() { const { startAngle, endAngle } = this; const midAngle = angleBetween(startAngle, endAngle); return normalizeAngle360(startAngle + midAngle / 2 + Math.PI / 2); } updatePath() { const { isBeveled } = this; if (isBeveled) { this.updateBeveledPath(); } else { this.updateRectangularPath(); } this.checkPathDirty(); } updateRectangularPath() { const { columnWidth, innerRadius, outerRadius, path } = this; const left = -columnWidth / 2; const right = columnWidth / 2; const top = -outerRadius; const bottom = -innerRadius; const rotation = this.getRotation(); const points = [ [left, bottom], [left, top], [right, top], [right, bottom] ].map(([x, y]) => rotatePoint(x, y, rotation)); path.clear(true); path.moveTo(points[0].x, points[0].y); path.lineTo(points[1].x, points[1].y); path.lineTo(points[2].x, points[2].y); path.lineTo(points[3].x, points[3].y); path.closePath(); } updateBeveledPath() { const { columnWidth, path, outerRadius, innerRadius, axisInnerRadius, axisOuterRadius, isRadiusAxisReversed } = this; const isStackBottom = isNumberEqual(innerRadius, axisInnerRadius); const sideRotation = Math.asin(columnWidth / 2 / innerRadius); const pointRotation = this.getRotation(); const rotate = (x, y) => rotatePoint(x, y, pointRotation); const getTriangleHypotenuse = (leg, otherLeg) => Math.sqrt(leg ** 2 + otherLeg ** 2); const getTriangleLeg = (hypotenuse, otherLeg) => { if (otherLeg > hypotenuse) { return 0; } return Math.sqrt(hypotenuse ** 2 - otherLeg ** 2); }; const compare = (value, otherValue, lessThan) => lessThan ? value < otherValue : value > otherValue; const shouldConnectBottomCircle = isStackBottom && !isNaN(sideRotation) && sideRotation < Math.PI / 6; let left = -columnWidth / 2; let right = columnWidth / 2; const top = -outerRadius; const bottom = -innerRadius * (shouldConnectBottomCircle ? Math.cos(sideRotation) : 1); const hasBottomIntersection = compare( axisOuterRadius, getTriangleHypotenuse(innerRadius, columnWidth / 2), !isRadiusAxisReversed ); if (hasBottomIntersection) { const bottomIntersectionX = getTriangleLeg(axisOuterRadius, innerRadius); left = -bottomIntersectionX; right = bottomIntersectionX; } path.clear(true); const bottomLeftPt = rotate(left, bottom); path.moveTo(bottomLeftPt.x, bottomLeftPt.y); const isEmpty2 = isNumberEqual(innerRadius, outerRadius); const hasSideIntersection = compare( axisOuterRadius, getTriangleHypotenuse(outerRadius, columnWidth / 2), !isRadiusAxisReversed ); if (isEmpty2 && shouldConnectBottomCircle) { path.arc( 0, 0, innerRadius, normalizeAngle360(-sideRotation - Math.PI / 2) + pointRotation, normalizeAngle360(sideRotation - Math.PI / 2) + pointRotation, false ); } else if (hasSideIntersection) { const sideIntersectionY = -getTriangleLeg(axisOuterRadius, columnWidth / 2); const topIntersectionX = getTriangleLeg(axisOuterRadius, outerRadius); if (!hasBottomIntersection) { const topLeftPt = rotate(left, sideIntersectionY); path.lineTo(topLeftPt.x, topLeftPt.y); } path.arc( 0, 0, axisOuterRadius, Math.atan2(sideIntersectionY, left) + pointRotation, Math.atan2(top, -topIntersectionX) + pointRotation, false ); if (!isNumberEqual(topIntersectionX, 0)) { const topRightBevelPt = rotate(topIntersectionX, top); path.lineTo(topRightBevelPt.x, topRightBevelPt.y); } path.arc( 0, 0, axisOuterRadius, Math.atan2(top, topIntersectionX) + pointRotation, Math.atan2(sideIntersectionY, right) + pointRotation, false ); } else { const topLeftPt = rotate(left, top); const topRightPt = rotate(right, top); path.lineTo(topLeftPt.x, topLeftPt.y); path.lineTo(topRightPt.x, topRightPt.y); } const bottomRightPt = rotate(right, bottom); path.lineTo(bottomRightPt.x, bottomRightPt.y); if (shouldConnectBottomCircle) { path.arc( 0, 0, innerRadius, normalizeAngle360(sideRotation - Math.PI / 2) + pointRotation, normalizeAngle360(-sideRotation - Math.PI / 2) + pointRotation, true ); } else { const rotatedBottomLeftPt = rotate(left, bottom); path.lineTo(rotatedBottomLeftPt.x, rotatedBottomLeftPt.y); } path.closePath(); } }; RadialColumnShape.className = "RadialColumnShape"; __decorateClass([ ScenePathChangeDetection() ], RadialColumnShape.prototype, "isBeveled", 2); __decorateClass([ ScenePathChangeDetection() ], RadialColumnShape.prototype, "columnWidth", 2); __decorateClass([ ScenePathChangeDetection() ], RadialColumnShape.prototype, "startAngle", 2); __decorateClass([ ScenePathChangeDetection() ], RadialColumnShape.prototype, "endAngle", 2); __decorateClass([ ScenePathChangeDetection() ], RadialColumnShape.prototype, "outerRadius", 2); __decorateClass([ ScenePathChangeDetection() ], RadialColumnShape.prototype, "innerRadius", 2); __decorateClass([ ScenePathChangeDetection() ], RadialColumnShape.prototype, "axisInnerRadius", 2); __decorateClass([ ScenePathChangeDetection() ], RadialColumnShape.prototype, "axisOuterRadius", 2); __decorateClass([ ScenePathChangeDetection() ], RadialColumnShape.prototype, "isRadiusAxisReversed", 2); function getRadialColumnWidth(startAngle, endAngle, axisOuterRadius, columnWidthRatio, maxColumnWidthRatio) { const rotation = angleBetween(startAngle, endAngle); const pad = rotation * (1 - columnWidthRatio) / 2; startAngle += pad; endAngle -= pad; if (rotation < 1e-3) { return 2 * axisOuterRadius * maxColumnWidthRatio; } if (rotation >= 2 * Math.PI) { const midAngle = startAngle + rotation / 2; startAngle = midAngle - Math.PI; endAngle = midAngle + Math.PI; } const startX = axisOuterRadius * Math.cos(startAngle); const startY = axisOuterRadius * Math.sin(startAngle); const endX = axisOuterRadius * Math.cos(endAngle); const endY = axisOuterRadius * Math.sin(endAngle); const colWidth = Math.floor(Math.sqrt((startX - endX) ** 2 + (startY - endY) ** 2)); const maxWidth = 2 * axisOuterRadius * maxColumnWidthRatio; return Math.max(1, Math.min(maxWidth, colWidth)); } // packages/ag-charts-community/src/scene/util/corner.ts var drawCorner = (path, { x0, y0, x1, y1, cx, cy }, cornerRadius, move) => { if (move) { path.moveTo(x0, y0); } if (x0 !== x1 || y0 !== y1) { const r0 = Math.atan2(y0 - cy, x0 - cx); const r1 = Math.atan2(y1 - cy, x1 - cx); path.arc(cx, cy, cornerRadius, r0, r1); } else { path.lineTo(x0, y0); } }; // packages/ag-charts-community/src/scene/shape/rect.ts var epsilon = 1e-6; var cornerEdges = (leadingEdge, trailingEdge, leadingInset, trailingInset, cornerRadius) => { let leadingClipped = false; let trailingClipped = false; let leading0 = trailingInset - Math.sqrt(Math.max(cornerRadius ** 2 - leadingInset ** 2, 0)); let leading1 = 0; let trailing0 = 0; let trailing1 = leadingInset - Math.sqrt(Math.max(cornerRadius ** 2 - trailingInset ** 2, 0)); if (leading0 > leadingEdge) { leadingClipped = true; leading0 = leadingEdge; leading1 = leadingInset - Math.sqrt(Math.max(cornerRadius ** 2 - (trailingInset - leadingEdge) ** 2)); } else if (leading0 < epsilon) { leading0 = 0; } if (trailing1 > trailingEdge) { trailingClipped = true; trailing0 = trailingInset - Math.sqrt(Math.max(cornerRadius ** 2 - (leadingInset - trailingEdge) ** 2)); trailing1 = trailingEdge; } else if (trailing1 < epsilon) { trailing1 = 0; } return { leading0, leading1, trailing0, trailing1, leadingClipped, trailingClipped }; }; var clippedRoundRect = (path, x, y, width, height, cornerRadii, clipBBox) => { let { topLeft: topLeftCornerRadius, topRight: topRightCornerRadius, bottomRight: bottomRightCornerRadius, bottomLeft: bottomLeftCornerRadius } = cornerRadii; const maxVerticalCornerRadius = Math.max( topLeftCornerRadius + bottomLeftCornerRadius, topRightCornerRadius + bottomRightCornerRadius ); const maxHorizontalCornerRadius = Math.max( topLeftCornerRadius + topRightCornerRadius, bottomLeftCornerRadius + bottomRightCornerRadius ); if (maxVerticalCornerRadius <= 0 && maxHorizontalCornerRadius <= 0) { if (clipBBox == null) { path.rect(x, y, width, height); } else { path.rect(clipBBox.x, clipBBox.y, clipBBox.width, clipBBox.height); } return; } else if (clipBBox == null && topLeftCornerRadius === topRightCornerRadius && topLeftCornerRadius === bottomRightCornerRadius && topLeftCornerRadius === bottomLeftCornerRadius) { path.roundRect(x, y, width, height, topLeftCornerRadius); return; } if (width < 0) { x += width; width = Math.abs(width); } if (height < 0) { y += height; height = Math.abs(height); } if (width <= 0 || height <= 0) return; if (clipBBox == null) { clipBBox = new BBox(x, y, width, height); } else { const x0 = Math.max(x, clipBBox.x); const x1 = Math.min(x + width, clipBBox.x + clipBBox.width); const y0 = Math.max(y, clipBBox.y); const y1 = Math.min(y + height, clipBBox.y + clipBBox.height); clipBBox = new BBox(x0, y0, x1 - x0, y1 - y0); } const borderScale = Math.max(maxVerticalCornerRadius / height, maxHorizontalCornerRadius / width, 1); if (borderScale > 1) { topLeftCornerRadius /= borderScale; topRightCornerRadius /= borderScale; bottomRightCornerRadius /= borderScale; bottomLeftCornerRadius /= borderScale; } let drawTopLeftCorner = true; let drawTopRightCorner = true; let drawBottomRightCorner = true; let drawBottomLeftCorner = true; let topLeftCorner; let topRightCorner; let bottomRightCorner; let bottomLeftCorner; if (drawTopLeftCorner) { const nodes = cornerEdges( clipBBox.height, clipBBox.width, Math.max(x + topLeftCornerRadius - clipBBox.x, 0), Math.max(y + topLeftCornerRadius - clipBBox.y, 0), topLeftCornerRadius ); if (nodes.leadingClipped) drawBottomLeftCorner = false; if (nodes.trailingClipped) drawTopRightCorner = false; const x0 = Math.max(clipBBox.x + nodes.leading1, clipBBox.x); const y0 = Math.max(clipBBox.y + nodes.leading0, clipBBox.y); const x1 = Math.max(clipBBox.x + nodes.trailing1, clipBBox.x); const y1 = Math.max(clipBBox.y + nodes.trailing0, clipBBox.y); const cx = x + topLeftCornerRadius; const cy = y + topLeftCornerRadius; topLeftCorner = { x0, y0, x1, y1, cx, cy }; } if (drawTopRightCorner) { const nodes = cornerEdges( clipBBox.width, clipBBox.height, Math.max(y + topRightCornerRadius - clipBBox.y, 0), Math.max(clipBBox.x + clipBBox.width - (x + width - topRightCornerRadius), 0), topRightCornerRadius ); if (nodes.leadingClipped) drawTopLeftCorner = false; if (nodes.trailingClipped) drawBottomRightCorner = false; const x0 = Math.min(clipBBox.x + clipBBox.width - nodes.leading0, clipBBox.x + clipBBox.width); const y0 = Math.max(clipBBox.y + nodes.leading1, clipBBox.y); const x1 = Math.min(clipBBox.x + clipBBox.width - nodes.trailing0, clipBBox.x + clipBBox.width); const y1 = Math.max(clipBBox.y + nodes.trailing1, clipBBox.y); const cx = x + width - topRightCornerRadius; const cy = y + topRightCornerRadius; topRightCorner = { x0, y0, x1, y1, cx, cy }; } if (drawBottomRightCorner) { const nodes = cornerEdges( clipBBox.height, clipBBox.width, Math.max(clipBBox.x + clipBBox.width - (x + width - bottomRightCornerRadius), 0), Math.max(clipBBox.y + clipBBox.height - (y + height - bottomRightCornerRadius), 0), bottomRightCornerRadius ); if (nodes.leadingClipped) drawTopRightCorner = false; if (nodes.trailingClipped) drawBottomLeftCorner = false; const x0 = Math.min(clipBBox.x + clipBBox.width - nodes.leading1, clipBBox.x + clipBBox.width); const y0 = Math.min(clipBBox.y + clipBBox.height - nodes.leading0, clipBBox.y + clipBBox.height); const x1 = Math.min(clipBBox.x + clipBBox.width - nodes.trailing1, clipBBox.x + clipBBox.width); const y1 = Math.min(clipBBox.y + clipBBox.height - nodes.trailing0, clipBBox.y + clipBBox.height); const cx = x + width - bottomRightCornerRadius; const cy = y + height - bottomRightCornerRadius; bottomRightCorner = { x0, y0, x1, y1, cx, cy }; } if (drawBottomLeftCorner) { const nodes = cornerEdges( clipBBox.width, clipBBox.height, Math.max(clipBBox.y + clipBBox.height - (y + height - bottomLeftCornerRadius), 0), Math.max(x + bottomLeftCornerRadius - clipBBox.x, 0), bottomLeftCornerRadius ); if (nodes.leadingClipped) drawBottomRightCorner = false; if (nodes.trailingClipped) drawTopLeftCorner = false; const x0 = Math.max(clipBBox.x + nodes.leading0, clipBBox.x); const y0 = Math.min(clipBBox.y + clipBBox.height - nodes.leading1, clipBBox.y + clipBBox.height); const x1 = Math.max(clipBBox.x + nodes.trailing0, clipBBox.x); const y1 = Math.min(clipBBox.y + clipBBox.height - nodes.trailing1, clipBBox.y + clipBBox.height); const cx = x + bottomLeftCornerRadius; const cy = y + height - bottomLeftCornerRadius; bottomLeftCorner = { x0, y0, x1, y1, cx, cy }; } let didMove = false; if (drawTopLeftCorner && topLeftCorner != null) { drawCorner(path, topLeftCorner, topLeftCornerRadius, !didMove); didMove || (didMove = true); } if (drawTopRightCorner && topRightCorner != null) { drawCorner(path, topRightCorner, topRightCornerRadius, !didMove); didMove || (didMove = true); } if (drawBottomRightCorner && bottomRightCorner != null) { drawCorner(path, bottomRightCorner, bottomRightCornerRadius, !didMove); didMove || (didMove = true); } if (drawBottomLeftCorner && bottomLeftCorner != null) { drawCorner(path, bottomLeftCorner, bottomLeftCornerRadius, !didMove); } path.closePath(); }; var Rect = class extends Path { constructor() { super(...arguments); this.borderPath = new ExtendedPath2D(); this.x = 0; this.y = 0; this.width = 10; this.height = 10; this.topLeftCornerRadius = 0; this.topRightCornerRadius = 0; this.bottomRightCornerRadius = 0; this.bottomLeftCornerRadius = 0; this.clipBBox = void 0; this.crisp = false; this.lastUpdatePathStrokeWidth = Shape.defaultStyles.strokeWidth; this.effectiveStrokeWidth = Shape.defaultStyles.strokeWidth; this.hittester = super.isPointInPath.bind(this); this.distanceCalculator = super.distanceSquaredTransformedPoint.bind(this); /** * When the rectangle's width or height is less than a pixel * and crisp mode is on, the rectangle will still fit into the pixel, * but will be less opaque to make an effect of holding less space. */ this.microPixelEffectOpacity = 1; } set cornerRadius(cornerRadius) { this.topLeftCornerRadius = cornerRadius; this.topRightCornerRadius = cornerRadius; this.bottomRightCornerRadius = cornerRadius; this.bottomLeftCornerRadius = cornerRadius; } isDirtyPath() { return this.lastUpdatePathStrokeWidth !== this.strokeWidth || Boolean(this.path.isDirty() || this.borderPath.isDirty()); } updatePath() { const { path, borderPath, crisp, topLeftCornerRadius: topLeft, topRightCornerRadius: topRight, bottomRightCornerRadius: bottomRight, bottomLeftCornerRadius: bottomLeft } = this; let { x, y, width: w, height: h, strokeWidth, clipBBox } = this; const pixelRatio = this.layerManager?.canvas.pixelRatio ?? 1; const pixelSize = 1 / pixelRatio; let microPixelEffectOpacity = 1; path.clear(true); borderPath.clear(true); if (crisp) { if (w <= pixelSize) { microPixelEffectOpacity *= w / pixelSize; } if (h <= pixelSize) { microPixelEffectOpacity *= h / pixelSize; } w = this.align(x, w); h = this.align(y, h); x = this.align(x); y = this.align(y); clipBBox = clipBBox != null ? new BBox( this.align(clipBBox.x), this.align(clipBBox.y), this.align(clipBBox.x, clipBBox.width), this.align(clipBBox.y, clipBBox.height) ) : void 0; } if (strokeWidth) { if (w < pixelSize) { const lx = x + pixelSize / 2; borderPath.moveTo(lx, y); borderPath.lineTo(lx, y + h); strokeWidth = pixelSize; this.borderClipPath = void 0; } else if (h < pixelSize) { const ly = y + pixelSize / 2; borderPath.moveTo(x, ly); borderPath.lineTo(x + w, ly); strokeWidth = pixelSize; this.borderClipPath = void 0; } else if (strokeWidth < w && strokeWidth < h) { const halfStrokeWidth = strokeWidth / 2; x += halfStrokeWidth; y += halfStrokeWidth; w -= strokeWidth; h -= strokeWidth; const adjustedClipBBox = clipBBox?.clone().shrink(halfStrokeWidth); const cornerRadii = { topLeft: topLeft > 0 ? topLeft - strokeWidth : 0, topRight: topRight > 0 ? topRight - strokeWidth : 0, bottomRight: bottomRight > 0 ? bottomRight - strokeWidth : 0, bottomLeft: bottomLeft > 0 ? bottomLeft - strokeWidth : 0 }; this.borderClipPath = void 0; if (w > 0 && h > 0 && (adjustedClipBBox == null || adjustedClipBBox?.width > 0 && adjustedClipBBox?.height > 0)) { clippedRoundRect(path, x, y, w, h, cornerRadii, adjustedClipBBox); clippedRoundRect(borderPath, x, y, w, h, cornerRadii, adjustedClipBBox); } } else { this.borderClipPath = this.borderClipPath ?? new ExtendedPath2D(); this.borderClipPath.clear(true); this.borderClipPath.rect(x, y, w, h); borderPath.rect(x, y, w, h); } } else { const cornerRadii = { topLeft, topRight, bottomRight, bottomLeft }; this.borderClipPath = void 0; clippedRoundRect(path, x, y, w, h, cornerRadii, clipBBox); } if ([topLeft, topRight, bottomRight, bottomLeft].every((r) => r === 0)) { const bbox = this.getBBox(); this.hittester = bbox.containsPoint.bind(bbox); this.distanceSquared = (hitX, hitY) => this.getBBox().distanceSquared(hitX, hitY); } else { this.hittester = super.isPointInPath; this.distanceCalculator = super.distanceSquaredTransformedPoint; } this.effectiveStrokeWidth = strokeWidth; this.lastUpdatePathStrokeWidth = strokeWidth; this.microPixelEffectOpacity = microPixelEffectOpacity; } computeBBox() { const { x, y, width, height, clipBBox } = this; return clipBBox?.clone() ?? new BBox(x, y, width, height); } isPointInPath(x, y) { return this.hittester(x, y); } get midPoint() { return { x: this.x + this.width / 2, y: this.y + this.height / 2 }; } distanceSquared(x, y) { return this.distanceCalculator(x, y); } applyFillAlpha(ctx) { const { fillOpacity, microPixelEffectOpacity, opacity } = this; ctx.globalAlpha *= opacity * fillOpacity * microPixelEffectOpacity; } renderStroke(ctx) { const { stroke, effectiveStrokeWidth } = this; if (stroke && effectiveStrokeWidth) { const { globalAlpha } = ctx; const { strokeOpacity, lineDash, lineDashOffset, lineCap, lineJoin, borderPath, borderClipPath, opacity, microPixelEffectOpacity } = this; if (borderClipPath) { ctx.clip(borderClipPath.getPath2D()); } this.applyStroke(ctx); ctx.globalAlpha *= opacity * strokeOpacity * microPixelEffectOpacity; ctx.lineWidth = effectiveStrokeWidth; if (lineDash) { ctx.setLineDash(lineDash); } if (lineDashOffset) { ctx.lineDashOffset = lineDashOffset; } if (lineCap) { ctx.lineCap = lineCap; } if (lineJoin) { ctx.lineJoin = lineJoin; } ctx.stroke(borderPath.getPath2D()); ctx.globalAlpha = globalAlpha; } } }; Rect.className = "Rect"; __decorateClass([ ScenePathChangeDetection() ], Rect.prototype, "x", 2); __decorateClass([ ScenePathChangeDetection() ], Rect.prototype, "y", 2); __decorateClass([ ScenePathChangeDetection() ], Rect.prototype, "width", 2); __decorateClass([ ScenePathChangeDetection() ], Rect.prototype, "height", 2); __decorateClass([ ScenePathChangeDetection() ], Rect.prototype, "topLeftCornerRadius", 2); __decorateClass([ ScenePathChangeDetection() ], Rect.prototype, "topRightCornerRadius", 2); __decorateClass([ ScenePathChangeDetection() ], Rect.prototype, "bottomRightCornerRadius", 2); __decorateClass([ ScenePathChangeDetection() ], Rect.prototype, "bottomLeftCornerRadius", 2); __decorateClass([ ScenePathChangeDetection() ], Rect.prototype, "clipBBox", 2); __decorateClass([ ScenePathChangeDetection() ], Rect.prototype, "crisp", 2); // packages/ag-charts-community/src/scene/sectorBox.ts var SectorBox = class _SectorBox { constructor(startAngle, endAngle, innerRadius, outerRadius) { this.startAngle = startAngle; this.endAngle = endAngle; this.innerRadius = innerRadius; this.outerRadius = outerRadius; } clone() { const { startAngle, endAngle, innerRadius, outerRadius } = this; return new _SectorBox(startAngle, endAngle, innerRadius, outerRadius); } equals(other) { return this.startAngle === other.startAngle && this.endAngle === other.endAngle && this.innerRadius === other.innerRadius && this.outerRadius === other.outerRadius; } [interpolate](other, d) { return new _SectorBox( this.startAngle * (1 - d) + other.startAngle * d, this.endAngle * (1 - d) + other.endAngle * d, this.innerRadius * (1 - d) + other.innerRadius * d, this.outerRadius * (1 - d) + other.outerRadius * d ); } }; // packages/ag-charts-community/src/scene/util/sector.ts function sectorBox({ startAngle, endAngle, innerRadius, outerRadius }) { let x0 = Infinity; let y0 = Infinity; let x1 = -Infinity; let y1 = -Infinity; const addPoint = (x, y) => { x0 = Math.min(x, x0); y0 = Math.min(y, y0); x1 = Math.max(x, x1); y1 = Math.max(y, y1); }; addPoint(innerRadius * Math.cos(startAngle), innerRadius * Math.sin(startAngle)); addPoint(innerRadius * Math.cos(endAngle), innerRadius * Math.sin(endAngle)); addPoint(outerRadius * Math.cos(startAngle), outerRadius * Math.sin(startAngle)); addPoint(outerRadius * Math.cos(endAngle), outerRadius * Math.sin(endAngle)); if (isBetweenAngles(0, startAngle, endAngle)) { addPoint(outerRadius, 0); } if (isBetweenAngles(Math.PI * 0.5, startAngle, endAngle)) { addPoint(0, outerRadius); } if (isBetweenAngles(Math.PI, startAngle, endAngle)) { addPoint(-outerRadius, 0); } if (isBetweenAngles(Math.PI * 1.5, startAngle, endAngle)) { addPoint(0, -outerRadius); } return new BBox(x0, y0, x1 - x0, y1 - y0); } function isPointInSector(x, y, sector) { const radius = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); const { innerRadius, outerRadius } = sector; if (sector.startAngle === sector.endAngle || radius < Math.min(innerRadius, outerRadius) || radius > Math.max(innerRadius, outerRadius)) { return false; } const startAngle = normalizeAngle180(sector.startAngle); const endAngle = normalizeAngle180(sector.endAngle); const angle = Math.atan2(y, x); return startAngle < endAngle ? angle <= endAngle && angle >= startAngle : angle <= endAngle && angle >= -Math.PI || angle >= startAngle && angle <= Math.PI; } function radiiScalingFactor(r, sweep, a, b) { if (a === 0 && b === 0) return 0; const fs1 = Math.asin(Math.abs(1 * a) / (r + 1 * a)) + Math.asin(Math.abs(1 * b) / (r + 1 * b)) - sweep; if (fs1 < 0) return 1; let start = 0; let end = 1; for (let i = 0; i < 8; i += 1) { const s = (start + end) / 2; const fs = Math.asin(Math.abs(s * a) / (r + s * a)) + Math.asin(Math.abs(s * b) / (r + s * b)) - sweep; if (fs < 0) { start = s; } else { end = s; } } return start; } var delta2 = 1e-6; function clockwiseAngle(angle, relativeToStartAngle) { if (angleBetween(angle, relativeToStartAngle) < delta2) { return relativeToStartAngle; } else { return normalizeAngle360(angle - relativeToStartAngle) + relativeToStartAngle; } } function clockwiseAngles(startAngle, endAngle, relativeToStartAngle = 0) { const fullPie = Math.abs(endAngle - startAngle) >= 2 * Math.PI; const sweepAngle = fullPie ? 2 * Math.PI : normalizeAngle360(endAngle - startAngle); startAngle = clockwiseAngle(startAngle, relativeToStartAngle); endAngle = startAngle + sweepAngle; return { startAngle, endAngle }; } function arcRadialLineIntersectionAngle(cx, cy, r, startAngle, endAngle, clipAngle) { const sinA = Math.sin(clipAngle); const cosA = Math.cos(clipAngle); const c = cx ** 2 + cy ** 2 - r ** 2; let p0x; let p0y; let p1x; let p1y; if (cosA > 0.5) { const tanA = sinA / cosA; const a = 1 + tanA ** 2; const b = -2 * (cx + cy * tanA); const d = b ** 2 - 4 * a * c; if (d < 0) return; const x0 = (-b + Math.sqrt(d)) / (2 * a); const x1 = (-b - Math.sqrt(d)) / (2 * a); p0x = x0; p0y = x0 * tanA; p1x = x1; p1y = x1 * tanA; } else { const cotA = cosA / sinA; const a = 1 + cotA ** 2; const b = -2 * (cy + cx * cotA); const d = b ** 2 - 4 * a * c; if (d < 0) return; const y0 = (-b + Math.sqrt(d)) / (2 * a); const y1 = (-b - Math.sqrt(d)) / (2 * a); p0x = y0 * cotA; p0y = y0; p1x = y1 * cotA; p1y = y1; } const normalisedX = cosA; const normalisedY = sinA; const p0DotNormalized = p0x * normalisedX + p0y * normalisedY; const p1DotNormalized = p1x * normalisedX + p1y * normalisedY; const a0 = p0DotNormalized > 0 ? clockwiseAngle(Math.atan2(p0y - cy, p0x - cx), startAngle) : NaN; const a1 = p1DotNormalized > 0 ? clockwiseAngle(Math.atan2(p1y - cy, p1x - cx), startAngle) : NaN; if (a0 >= startAngle && a0 <= endAngle) { return a0; } else if (a1 >= startAngle && a1 <= endAngle) { return a1; } } function arcCircleIntersectionAngle(cx, cy, r, startAngle, endAngle, circleR) { const d = Math.hypot(cx, cy); const d1 = (d ** 2 - r ** 2 + circleR ** 2) / (2 * d); const d2 = d - d1; const theta = Math.atan2(cy, cx); const deltaTheta = Math.acos(-d2 / r); const a0 = clockwiseAngle(theta + deltaTheta, startAngle); const a1 = clockwiseAngle(theta - deltaTheta, startAngle); if (a0 >= startAngle && a0 <= endAngle) { return a0; } else if (a1 >= startAngle && a1 <= endAngle) { return a1; } } // packages/ag-charts-community/src/scene/shape/sector.ts var Arc2 = class { constructor(cx, cy, r, a0, a1) { this.cx = cx; this.cy = cy; this.r = r; this.a0 = a0; this.a1 = a1; if (this.a0 >= this.a1) { this.a0 = NaN; this.a1 = NaN; } } isValid() { return Number.isFinite(this.a0) && Number.isFinite(this.a1); } pointAt(a) { return { x: this.cx + this.r * Math.cos(a), y: this.cy + this.r * Math.sin(a) }; } clipStart(a) { if (a == null || !this.isValid() || a < this.a0) return; this.a0 = a; if (Number.isNaN(a) || this.a0 >= this.a1) { this.a0 = NaN; this.a1 = NaN; } } clipEnd(a) { if (a == null || !this.isValid() || a > this.a1) return; this.a1 = a; if (Number.isNaN(a) || this.a0 >= this.a1) { this.a0 = NaN; this.a1 = NaN; } } }; var Sector = class extends Path { constructor() { super(...arguments); this.centerX = 0; this.centerY = 0; this.innerRadius = 10; this.outerRadius = 20; this.startAngle = 0; this.endAngle = Math.PI * 2; this.clipSector = void 0; this.concentricEdgeInset = 0; this.radialEdgeInset = 0; this.startOuterCornerRadius = 0; this.endOuterCornerRadius = 0; this.startInnerCornerRadius = 0; this.endInnerCornerRadius = 0; } set inset(value) { this.concentricEdgeInset = value; this.radialEdgeInset = value; } set cornerRadius(value) { this.startOuterCornerRadius = value; this.endOuterCornerRadius = value; this.startInnerCornerRadius = value; this.endInnerCornerRadius = value; } computeBBox() { return sectorBox(this).translate(this.centerX, this.centerY); } normalizedRadii() { const { concentricEdgeInset } = this; let { innerRadius, outerRadius } = this; innerRadius = innerRadius > 0 ? innerRadius + concentricEdgeInset : 0; outerRadius = Math.max(outerRadius - concentricEdgeInset, 0); return { innerRadius, outerRadius }; } normalizedClipSector() { const { clipSector } = this; if (clipSector == null) return; const { startAngle, endAngle } = clockwiseAngles(this.startAngle, this.endAngle); const { innerRadius, outerRadius } = this.normalizedRadii(); const clipAngles = clockwiseAngles(clipSector.startAngle, clipSector.endAngle, startAngle); return new SectorBox( Math.max(startAngle, clipAngles.startAngle), Math.min(endAngle, clipAngles.endAngle), Math.max(innerRadius, clipSector.innerRadius), Math.min(outerRadius, clipSector.outerRadius) ); } getAngleOffset(radius) { return radius > 0 ? this.radialEdgeInset / radius : 0; } arc(r, angleSweep, a0, a1, outerArc, innerArc, start, inner) { if (r <= 0) return; const { startAngle, endAngle } = clockwiseAngles(this.startAngle, this.endAngle); const { innerRadius, outerRadius } = this.normalizedRadii(); const clipSector = this.normalizedClipSector(); if (inner && innerRadius <= 0) return; const angleOffset = inner ? this.getAngleOffset(innerRadius + r) : this.getAngleOffset(outerRadius - r); const angle = start ? startAngle + angleOffset + angleSweep : endAngle - angleOffset - angleSweep; const radius = inner ? innerRadius + r : outerRadius - r; const cx = radius * Math.cos(angle); const cy = radius * Math.sin(angle); if (clipSector != null) { const delta3 = 1e-6; if (!start && !(angle >= startAngle - delta3 && angle <= clipSector.endAngle - delta3)) return; if (start && !(angle >= clipSector.startAngle + delta3 && angle <= endAngle - delta3)) return; if (inner && radius < clipSector.innerRadius - delta3) return; if (!inner && radius > clipSector.outerRadius + delta3) return; } const arc = new Arc2(cx, cy, r, a0, a1); if (clipSector != null) { if (inner) { arc.clipStart(arcRadialLineIntersectionAngle(cx, cy, r, a0, a1, clipSector.endAngle)); arc.clipEnd(arcRadialLineIntersectionAngle(cx, cy, r, a0, a1, clipSector.startAngle)); } else { arc.clipStart(arcRadialLineIntersectionAngle(cx, cy, r, a0, a1, clipSector.startAngle)); arc.clipEnd(arcRadialLineIntersectionAngle(cx, cy, r, a0, a1, clipSector.endAngle)); } let circleClipStart; let circleClipEnd; if (start) { circleClipStart = arcCircleIntersectionAngle(cx, cy, r, a0, a1, clipSector.innerRadius); circleClipEnd = arcCircleIntersectionAngle(cx, cy, r, a0, a1, clipSector.outerRadius); } else { circleClipStart = arcCircleIntersectionAngle(cx, cy, r, a0, a1, clipSector.outerRadius); circleClipEnd = arcCircleIntersectionAngle(cx, cy, r, a0, a1, clipSector.innerRadius); } arc.clipStart(circleClipStart); arc.clipEnd(circleClipEnd); if (circleClipStart != null) { const { x: x2, y: y2 } = arc.pointAt(circleClipStart); const theta2 = clockwiseAngle(Math.atan2(y2, x2), startAngle); if (start) { innerArc?.clipStart(theta2); } else { outerArc.clipEnd(theta2); } } if (circleClipEnd != null) { const { x: x2, y: y2 } = arc.pointAt(circleClipEnd); const theta2 = clockwiseAngle(Math.atan2(y2, x2), startAngle); if (start) { outerArc.clipStart(theta2); } else { innerArc?.clipEnd(theta2); } } } if (clipSector != null) { const { x: x2, y: y2 } = arc.pointAt((arc.a0 + arc.a1) / 2); if (!isPointInSector(x2, y2, clipSector)) return; } const { x, y } = arc.pointAt(start === inner ? arc.a0 : arc.a1); const theta = clockwiseAngle(Math.atan2(y, x), startAngle); const radialArc = inner ? innerArc : outerArc; if (start) { radialArc?.clipStart(theta); } else { radialArc?.clipEnd(theta); } return arc; } updatePath() { const delta3 = 1e-6; const { path, centerX, centerY, concentricEdgeInset, radialEdgeInset } = this; let { startOuterCornerRadius, endOuterCornerRadius, startInnerCornerRadius, endInnerCornerRadius } = this; const { startAngle, endAngle } = clockwiseAngles(this.startAngle, this.endAngle); const { innerRadius, outerRadius } = this.normalizedRadii(); const clipSector = this.normalizedClipSector(); const sweepAngle = endAngle - startAngle; const fullPie = sweepAngle >= 2 * Math.PI - delta3; path.clear(); if (innerRadius === 0 && outerRadius === 0 || innerRadius > outerRadius) { return; } else if ((clipSector?.startAngle ?? startAngle) === (clipSector?.endAngle ?? endAngle)) { return; } else if (fullPie && this.clipSector == null && startOuterCornerRadius === 0 && endOuterCornerRadius === 0 && startInnerCornerRadius === 0 && endInnerCornerRadius === 0) { path.moveTo(centerX + outerRadius * Math.cos(startAngle), centerY + outerRadius * Math.sin(startAngle)); path.arc(centerX, centerY, outerRadius, startAngle, endAngle); if (innerRadius > concentricEdgeInset) { path.moveTo(centerX + innerRadius * Math.cos(endAngle), centerY + innerRadius * Math.sin(endAngle)); path.arc(centerX, centerY, innerRadius, endAngle, startAngle, true); } path.closePath(); return; } else if (this.clipSector == null && Math.abs(innerRadius - outerRadius) < 1e-6) { path.arc(centerX, centerY, outerRadius, startAngle, endAngle, false); path.arc(centerX, centerY, outerRadius, endAngle, startAngle, true); path.closePath(); return; } const innerAngleOffset = this.getAngleOffset(innerRadius); const outerAngleOffset = this.getAngleOffset(outerRadius); const outerAngleExceeded = sweepAngle < 2 * outerAngleOffset; if (outerAngleExceeded) return; const hasInnerSweep = (clipSector?.innerRadius ?? innerRadius) > concentricEdgeInset; const innerAngleExceeded = innerRadius < concentricEdgeInset || sweepAngle < 2 * innerAngleOffset; const radialLength = outerRadius - innerRadius; const maxRadialLength = Math.max( startOuterCornerRadius, startInnerCornerRadius, endOuterCornerRadius, endInnerCornerRadius ); const initialScalingFactor = maxRadialLength > 0 ? Math.min(radialLength / maxRadialLength, 1) : 1; startOuterCornerRadius *= initialScalingFactor; endOuterCornerRadius *= initialScalingFactor; startInnerCornerRadius *= initialScalingFactor; endInnerCornerRadius *= initialScalingFactor; const outerScalingFactor = radiiScalingFactor( outerRadius, sweepAngle - 2 * outerAngleOffset, -startOuterCornerRadius, -endOuterCornerRadius ); startOuterCornerRadius *= outerScalingFactor; endOuterCornerRadius *= outerScalingFactor; if (!innerAngleExceeded && hasInnerSweep) { const innerScalingFactor = radiiScalingFactor( innerRadius, sweepAngle - 2 * innerAngleOffset, startInnerCornerRadius, endInnerCornerRadius ); startInnerCornerRadius *= innerScalingFactor; endInnerCornerRadius *= innerScalingFactor; } else { startInnerCornerRadius = 0; endInnerCornerRadius = 0; } const maxCombinedRadialLength = Math.max( startOuterCornerRadius + startInnerCornerRadius, endOuterCornerRadius + endInnerCornerRadius ); const edgesScalingFactor = maxCombinedRadialLength > 0 ? Math.min(radialLength / maxCombinedRadialLength, 1) : 1; startOuterCornerRadius *= edgesScalingFactor; endOuterCornerRadius *= edgesScalingFactor; startInnerCornerRadius *= edgesScalingFactor; endInnerCornerRadius *= edgesScalingFactor; let startOuterCornerRadiusAngleSweep = 0; let endOuterCornerRadiusAngleSweep = 0; const startOuterCornerRadiusSweep = startOuterCornerRadius / (outerRadius - startOuterCornerRadius); const endOuterCornerRadiusSweep = endOuterCornerRadius / (outerRadius - endOuterCornerRadius); if (startOuterCornerRadiusSweep >= 0 && startOuterCornerRadiusSweep < 1 - delta3) { startOuterCornerRadiusAngleSweep = Math.asin(startOuterCornerRadiusSweep); } else { startOuterCornerRadiusAngleSweep = sweepAngle / 2; const maxStartOuterCornerRadius = outerRadius / (1 / Math.sin(startOuterCornerRadiusAngleSweep) + 1); startOuterCornerRadius = Math.min(maxStartOuterCornerRadius, startOuterCornerRadius); } if (endOuterCornerRadiusSweep >= 0 && endOuterCornerRadiusSweep < 1 - delta3) { endOuterCornerRadiusAngleSweep = Math.asin(endOuterCornerRadiusSweep); } else { endOuterCornerRadiusAngleSweep = sweepAngle / 2; const maxEndOuterCornerRadius = outerRadius / (1 / Math.sin(endOuterCornerRadiusAngleSweep) + 1); endOuterCornerRadius = Math.min(maxEndOuterCornerRadius, endOuterCornerRadius); } const startInnerCornerRadiusAngleSweep = Math.asin( startInnerCornerRadius / (innerRadius + startInnerCornerRadius) ); const endInnerCornerRadiusAngleSweep = Math.asin(endInnerCornerRadius / (innerRadius + endInnerCornerRadius)); const outerArcRadius = clipSector?.outerRadius ?? outerRadius; const outerArcRadiusOffset = this.getAngleOffset(outerArcRadius); const outerArc = new Arc2( 0, 0, outerArcRadius, startAngle + outerArcRadiusOffset, endAngle - outerArcRadiusOffset ); const innerArcRadius = clipSector?.innerRadius ?? innerRadius; const innerArcRadiusOffset = this.getAngleOffset(innerArcRadius); const innerArc = hasInnerSweep ? new Arc2(0, 0, innerArcRadius, startAngle + innerArcRadiusOffset, endAngle - innerArcRadiusOffset) : void 0; if (clipSector != null) { outerArc.clipStart(clipSector.startAngle); outerArc.clipEnd(clipSector.endAngle); innerArc?.clipStart(clipSector.startAngle); innerArc?.clipEnd(clipSector.endAngle); } const startOuterArc = this.arc( startOuterCornerRadius, startOuterCornerRadiusAngleSweep, startAngle - Math.PI * 0.5, startAngle + startOuterCornerRadiusAngleSweep, outerArc, innerArc, true, false ); const endOuterArc = this.arc( endOuterCornerRadius, endOuterCornerRadiusAngleSweep, endAngle - endOuterCornerRadiusAngleSweep, endAngle + Math.PI * 0.5, outerArc, innerArc, false, false ); const endInnerArc = this.arc( endInnerCornerRadius, endInnerCornerRadiusAngleSweep, endAngle + Math.PI * 0.5, endAngle + Math.PI - endInnerCornerRadiusAngleSweep, outerArc, innerArc, false, true ); const startInnerArc = this.arc( startInnerCornerRadius, startInnerCornerRadiusAngleSweep, startAngle + Math.PI + startInnerCornerRadiusAngleSweep, startAngle + Math.PI * 1.5, outerArc, innerArc, true, true ); if (innerAngleExceeded) { const x = sweepAngle < Math.PI * 0.5 ? radialEdgeInset * (1 + Math.cos(sweepAngle)) / Math.sin(sweepAngle) : NaN; let r; if (x > 0 && x < outerRadius) { r = Math.max(Math.hypot(radialEdgeInset, x), innerRadius); } else { r = radialEdgeInset; } r = Math.max(r, innerRadius); const midAngle = startAngle + sweepAngle * 0.5; path.moveTo(centerX + r * Math.cos(midAngle), centerY + r * Math.sin(midAngle)); } else if (startInnerArc?.isValid() === true || innerArc?.isValid() === true) { } else { const midAngle = startAngle + sweepAngle / 2; const cx = innerRadius * Math.cos(midAngle); const cy = innerRadius * Math.sin(midAngle); path.moveTo(centerX + cx, centerY + cy); } if (startOuterArc?.isValid() === true) { const { cx, cy, r, a0, a1 } = startOuterArc; path.arc(centerX + cx, centerY + cy, r, a0, a1); } if (outerArc.isValid()) { const { r, a0, a1 } = outerArc; path.arc(centerX, centerY, r, a0, a1); } if (endOuterArc?.isValid() === true) { const { cx, cy, r, a0, a1 } = endOuterArc; path.arc(centerX + cx, centerY + cy, r, a0, a1); } if (!innerAngleExceeded) { if (endInnerArc?.isValid() === true) { const { cx, cy, r, a0, a1 } = endInnerArc; path.arc(centerX + cx, centerY + cy, r, a0, a1); } if (innerArc?.isValid() === true) { const { r, a0, a1 } = innerArc; path.arc(centerX, centerY, r, a1, a0, true); } if (startInnerArc?.isValid() === true) { const { cx, cy, r, a0, a1 } = startInnerArc; path.arc(centerX + cx, centerY + cy, r, a0, a1); } } path.closePath(); } isPointInPath(x, y) { const { startAngle, endAngle, innerRadius, outerRadius } = this.clipSector ?? this; return isPointInSector(x - this.centerX, y - this.centerY, { startAngle, endAngle, innerRadius: Math.min(innerRadius, outerRadius), outerRadius: Math.max(innerRadius, outerRadius) }); } }; Sector.className = "Sector"; __decorateClass([ ScenePathChangeDetection() ], Sector.prototype, "centerX", 2); __decorateClass([ ScenePathChangeDetection() ], Sector.prototype, "centerY", 2); __decorateClass([ ScenePathChangeDetection() ], Sector.prototype, "innerRadius", 2); __decorateClass([ ScenePathChangeDetection() ], Sector.prototype, "outerRadius", 2); __decorateClass([ ScenePathChangeDetection() ], Sector.prototype, "startAngle", 2); __decorateClass([ ScenePathChangeDetection() ], Sector.prototype, "endAngle", 2); __decorateClass([ ScenePathChangeDetection() ], Sector.prototype, "clipSector", 2); __decorateClass([ ScenePathChangeDetection() ], Sector.prototype, "concentricEdgeInset", 2); __decorateClass([ ScenePathChangeDetection() ], Sector.prototype, "radialEdgeInset", 2); __decorateClass([ ScenePathChangeDetection() ], Sector.prototype, "startOuterCornerRadius", 2); __decorateClass([ ScenePathChangeDetection() ], Sector.prototype, "endOuterCornerRadius", 2); __decorateClass([ ScenePathChangeDetection() ], Sector.prototype, "startInnerCornerRadius", 2); __decorateClass([ ScenePathChangeDetection() ], Sector.prototype, "endInnerCornerRadius", 2);