ag-charts-community
Version:
Advanced Charting / Charts supporting Javascript / Typescript / React / Angular / Vue
8,493 lines • 270 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
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/chart/caption.ts
import { createId as createId2, isArray as isArray6, toPlainText as toPlainText2 } from "ag-charts-core";
// packages/ag-charts-community/src/scene/node.ts
import { Logger, createId, createSvgElement } from "ag-charts-core";
// packages/ag-charts-community/src/util/object.ts
import { entries, isArray, isObject, isPlainObject } from "ag-charts-core";
// packages/ag-charts-community/src/util/decorator.ts
var BREAK_TRANSFORM_CHAIN = Symbol("BREAK");
var CONFIG_KEY = "__decorator_config";
var ACCESSORS_KEY = "__decorator_accessors";
function addFakeTransformToInstanceProperty(target, propertyKeyOrSymbol) {
initialiseConfig(target, propertyKeyOrSymbol).optional = true;
}
function initialiseConfig(target, propertyKeyOrSymbol) {
if (Object.getOwnPropertyDescriptor(target, CONFIG_KEY) == null) {
Object.defineProperty(target, CONFIG_KEY, { value: {} });
}
if (Object.getOwnPropertyDescriptor(target, ACCESSORS_KEY) == null) {
const parentAccessors = Object.getPrototypeOf(target)?.[ACCESSORS_KEY];
const accessors = parentAccessors?.slice() ?? [];
Object.defineProperty(target, ACCESSORS_KEY, { value: accessors });
}
const config = target[CONFIG_KEY];
const propertyKey = propertyKeyOrSymbol.toString();
if (config[propertyKey] != null) {
return config[propertyKey];
}
config[propertyKey] = { setters: [], getters: [], observers: [] };
const descriptor = Object.getOwnPropertyDescriptor(target, propertyKeyOrSymbol);
let prevGet = descriptor?.get;
let prevSet = descriptor?.set;
if (prevGet == null || prevSet == null) {
const accessors = target[ACCESSORS_KEY];
let index = accessors.indexOf(propertyKeyOrSymbol);
if (index === -1) {
index = accessors.push(propertyKeyOrSymbol) - 1;
}
prevGet ?? (prevGet = function() {
let accessorValues = this.__accessors;
if (accessorValues == null) {
accessorValues = accessors.slice().fill(void 0);
Object.defineProperty(this, "__accessors", { value: accessorValues });
}
return accessorValues[index];
});
prevSet ?? (prevSet = function(value) {
let accessorValues = this.__accessors;
if (accessorValues == null) {
accessorValues = accessors.slice().fill(void 0);
Object.defineProperty(this, "__accessors", { value: accessorValues });
}
accessorValues[index] = value;
});
}
const getter = function() {
let value = prevGet.call(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.call(this);
}
for (const transformFn of setters) {
value = transformFn(this, propertyKeyOrSymbol, value, oldValue);
if (value === BREAK_TRANSFORM_CHAIN) {
return;
}
}
prevSet.call(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));
}
// packages/ag-charts-community/src/util/object.ts
function objectsEqual(a, b) {
if (Array.isArray(a)) {
if (!Array.isArray(b))
return false;
if (a.length !== b.length)
return false;
return a.every((av, i) => objectsEqual(av, b[i]));
} else if (isPlainObject(a)) {
if (!isPlainObject(b))
return false;
return objectsEqualWith(a, b, objectsEqual);
}
return a === b;
}
function objectsEqualWith(a, b, cmp2) {
if (Object.is(a, b))
return true;
for (const key of Object.keys(b)) {
if (!(key in a))
return false;
}
for (const key of Object.keys(a)) {
if (!(key in b))
return false;
if (!cmp2(a[key], b[key]))
return false;
}
return true;
}
function mergeDefaults(...sources) {
const target = {};
for (const source of sources) {
if (!isObject(source))
continue;
const keys = isDecoratedObject(source) ? listDecoratedProperties(source) : Object.keys(source);
for (const key of keys) {
if (isPlainObject(target[key]) && isPlainObject(source[key])) {
target[key] = mergeDefaults(target[key], source[key]);
} else {
target[key] ?? (target[key] = source[key]);
}
}
}
return target;
}
function merge(...sources) {
const target = {};
for (const source of sources) {
if (!isObject(source))
continue;
const keys = isDecoratedObject(source) ? listDecoratedProperties(source) : Object.keys(source);
for (const key of keys) {
if (isPlainObject(target[key]) && isPlainObject(source[key])) {
target[key] = merge(target[key], source[key]);
} else if (!(key in target)) {
target[key] ?? (target[key] = source[key]);
}
}
}
return target;
}
// packages/ag-charts-community/src/scene/bbox.ts
import { boxContains, boxesEqual, clamp } from "ag-charts-core";
// 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/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 boxesEqual(this, other);
}
containsPoint(x, y) {
return boxContains(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 of Object.keys(amount)) {
const value = amount[key];
if (typeof value === "number") {
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 of Object.keys(amount)) {
const value = amount[key];
if (typeof value === "number") {
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
import { arraysEqual } from "ag-charts-core";
var TRIPLE_EQ = (lhs, rhs) => lhs === rhs;
function SceneChangeDetection(opts) {
return function(target, key) {
const privateKey = `__${key}`;
if (target[key])
return;
prepareGetSet(target, key, privateKey, opts);
};
}
function SceneRefChangeDetection(opts) {
return SceneChangeDetection(opts);
}
function SceneObjectChangeDetection(opts) {
return SceneChangeDetection(opts);
}
function SceneArrayChangeDetection(opts) {
const baseOpts = opts ?? {};
baseOpts.equals = arraysEqual;
return SceneChangeDetection(opts);
}
function prepareGetSet(target, key, privateKey, opts) {
const { changeCb, convertor, checkDirtyOnAssignment = false } = opts ?? {};
const requiredOpts = { changeCb, checkDirtyOnAssignment, convertor };
const setter = buildCheckDirtyChain(
privateKey,
buildChangeCallbackChain(
buildConvertorChain(buildSetter(privateKey, requiredOpts), 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(privateKey, setterFn, opts) {
const { checkDirtyOnAssignment } = opts;
if (checkDirtyOnAssignment) {
return function(value) {
const change = setterFn.call(this, value);
if (value?._dirty === true) {
this.markDirty(privateKey);
}
return change;
};
}
return setterFn;
}
function buildSetter(privateKey, opts) {
const { equals = TRIPLE_EQ } = opts;
return function(value) {
const oldValue = this[privateKey];
if (!equals(value, oldValue)) {
this[privateKey] = value;
this.onChangeDetection(privateKey);
return value;
}
return NO_CHANGE;
};
}
// 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/node.ts
var MAX_ERROR_COUNT = 5;
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.name = void 0;
this.transitionOut = void 0;
this.pointerEvents = 0 /* All */;
this._datum = void 0;
this._previousDatum = void 0;
this.scene = void 0;
this._debugDirtyProperties = void 0;
this.parentNode = void 0;
this.cachedBBox = void 0;
/**
* 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.batchLevel = 0;
this.batchDirty = false;
this.name = options?.name;
this.tag = options?.tag ?? NaN;
this.zIndex = options?.zIndex ?? 0;
this.scene = options?.scene;
if (options?.debugDirty ?? _Node._debugEnabled) {
this._debugDirtyProperties = /* @__PURE__ */ new Map([["__first__", []]]);
}
}
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}`);
root.setAttribute("overflow", "visible");
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;
}
set datum(datum) {
if (this._datum !== datum) {
this._previousDatum = this._datum;
this._datum = datum;
}
}
get previousDatum() {
return this._previousDatum;
}
get layerManager() {
return this.scene?.layersManager;
}
get imageLoader() {
return this.scene?.imageLoader;
}
closestDatum() {
for (const { datum } of this.traverseUp(true)) {
if (datum != null) {
return datum;
}
}
}
/** 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;
if (this.batchLevel > 0 || this.batchDirty) {
throw new Error("AG Charts - illegal rendering state; batched update in progress");
}
return this.childNodeCounts;
}
/** Guaranteed isolated render - if there is any failure, the Cavans2D context is returned to its prior state. */
isolatedRender(renderCtx) {
renderCtx.ctx.save();
try {
this.render(renderCtx);
} catch (e) {
const errorCount = e.errorCount ?? 1;
if (errorCount >= MAX_ERROR_COUNT) {
e.errorCount = errorCount;
throw e;
}
Logger.warnOnce("Error during rendering", e, e.stack);
} finally {
renderCtx.ctx.restore();
}
}
render(renderCtx) {
const { stats } = renderCtx;
this.debugDirtyProperties();
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;
}
}
setScene(scene) {
this.scene = scene;
}
*traverseUp(includeSelf) {
let node = this;
if (includeSelf) {
yield node;
}
while (node = node.parentNode) {
yield node;
}
}
/**
* Checks if the node is the root (has no parent).
*/
isRoot() {
return !this.parentNode;
}
removeChild(node) {
throw new Error(
`AG Charts - internal error, unknown child node ${node.name ?? node.id} in $${this.name ?? this.id}`
);
}
remove() {
this.parentNode?.removeChild(this);
}
destroy() {
this.parentNode?.removeChild(this);
}
batchedUpdate(fn) {
this.batchLevel++;
fn();
this.batchLevel--;
if (this.batchLevel === 0 && this.batchDirty) {
this.markDirty();
this.batchDirty = false;
}
}
setProperties(styles) {
this.batchLevel++;
Object.assign(this, styles);
this.batchLevel--;
if (this.batchLevel === 0 && this.batchDirty) {
this.markDirty();
this.batchDirty = false;
}
return this;
}
containsPoint(_x, _y) {
return false;
}
pickNode(x, y) {
if (this.containsPoint(x, y)) {
return this;
}
}
pickNodes(x, y, into = []) {
if (this.containsPoint(x, y)) {
into.push(this);
}
return into;
}
getBBox() {
this.cachedBBox ?? (this.cachedBBox = Object.freeze(this.computeBBox()));
return this.cachedBBox;
}
computeBBox() {
return;
}
onChangeDetection(property) {
this.markDirty(property);
}
markDirtyChildrenOrder() {
this.cachedBBox = void 0;
}
markDirty(property) {
if (this.batchLevel > 0) {
this.batchDirty = true;
return;
}
if (property != null && this._debugDirtyProperties) {
this.markDebugProperties(property);
}
this.cachedBBox = void 0;
this.parentNode?.markDirty();
}
markDebugProperties(property) {
const sources = this._debugDirtyProperties?.get(property) ?? [];
const caller = new Error().stack?.split("\n").filter((line) => {
return line !== "Error" && !line.includes(".markDebugProperties") && !line.includes(".markDirty") && !line.includes("Object.assign ") && !line.includes(`${this.constructor.name}.`);
}) ?? "unknown";
sources.push(caller[0].replace(" at ", "").trim());
this._debugDirtyProperties?.set(property, sources);
}
debugDirtyProperties() {
if (this._debugDirtyProperties == null)
return;
if (!this._debugDirtyProperties.has("__first__")) {
this._debugDirtyProperties.forEach((sources, property) => {
if (sources.length > 1) {
console.groupCollapsed(
`Property changed multiple times before render: ${this.constructor.name}.${property} (${sources.length}x)`
);
sources.forEach((source) => console.log(source));
console.groupEnd();
}
});
}
this._debugDirtyProperties.clear();
}
onZIndexChange() {
this.parentNode?.markDirtyChildrenOrder();
}
toSVG() {
return;
}
};
_Node._nextSerialNumber = 0;
// eslint-disable-next-line sonarjs/public-static-readonly
_Node._debugEnabled = false;
__decorateClass([
SceneChangeDetection()
], _Node.prototype, "visible", 2);
__decorateClass([
SceneObjectChangeDetection({
equals: objectsEqual,
changeCb: (target) => target.onZIndexChange()
})
], _Node.prototype, "zIndex", 2);
var Node = _Node;
// packages/ag-charts-community/src/scene/shape/text.ts
import { createSvgElement as createSvgElement10, isArray as isArray4, isString as isString3, toPlainText } from "ag-charts-core";
// packages/ag-charts-community/src/util/debug.ts
import { Logger as Logger2, getWindow, toArray } from "ag-charts-core";
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);
Logger2.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();
Logger2.log(...logContent);
}
};
return Object.assign(resultFn, {
check: () => Debug.check(...debugSelectors),
group: (name, cb) => {
if (Debug.check(...debugSelectors)) {
return Logger2.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));
},
inDevelopmentMode(fn) {
if (Debug.check("dev")) {
return fn();
}
}
};
// 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 = TextUtils.toFontString(options.font);
const key = `${font}-${options.textAlign ?? "start"}-${options.textBaseline ?? "top"}`;
return this.instanceMap.get(key) ?? this.createFontMeasurer(font, options, key);
}
static clear() {
this.instanceMap.clear();
}
// 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 ?? "top";
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 = 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 }) {
let fontString = "";
if (fontStyle && fontStyle !== "normal") {
fontString += `${fontStyle} `;
}
if (fontWeight && fontWeight !== "normal" && fontWeight !== 400) {
fontString += `${fontWeight} `;
}
fontString += `${fontSize}px`;
fontString += ` ${fontFamily}`;
return fontString.trim();
}
static getLineHeight(fontSize) {
return Math.ceil(fontSize * this.defaultLineHeight);
}
static getHorizontalModifier(textAlign) {
switch (textAlign) {
case "left":
case "start":
return 0;
case "center":
return 0.5;
case "right":
case "end":
return 1;
default:
return 0;
}
}
// 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
};
}
// Calculates aggregated metrics for multiline text.
getMultilineMetrics(lines) {
let width = 0;
let height = 0;
let offsetTop = 0;
let offsetLeft = 0;
let baselineDistance = 0;
let alphabeticBaseline = 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;
alphabeticBaseline = m.alphabeticBaseline;
} 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, alphabeticBaseline, 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/group.ts
import { clamp as clamp5, toIterable } from "ag-charts-core";
// packages/ag-charts-community/src/scene/canvas/canvasUtil.ts
function clearContext({
context,
pixelRatio,
width,
height
}) {
context.save();
try {
context.resetTransform();
context.clearRect(0, 0, Math.ceil(width * pixelRatio), Math.ceil(height * pixelRatio));
} finally {
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.floor(width * pixelRatio), Math.floor(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/shape/shape.ts
import { boxesEqual as boxesEqual2, clamp as clamp4, generateUUID, isString } from "ag-charts-core";
// packages/ag-charts-community/src/scene/gradient/conicGradient.ts
import { createSvgElement as createSvgElement3 } from "ag-charts-core";
// 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 startAngle !== endAngle;
}
}
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);
}
function normalizeAngle360FromDegrees(degrees) {
return degrees ? normalizeAngle360(toRadians(degrees)) : 0;
}
// packages/ag-charts-community/src/scene/gradient/gradient.ts
import { createSvgElement as createSvgElement2 } from "ag-charts-core";
// packages/ag-charts-community/src/scale/colorScale.ts
import { Logger as Logger3, clamp as clamp3 } from "ag-charts-core";
// packages/ag-charts-community/src/util/color.ts
import { clamp as clamp2 } from "ag-charts-core";
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 = clamp2(0, r || 0, 1);
this.g = clamp2(0, g || 0, 1);
this.b = clamp2(0, b || 0, 1);
this.a = clamp2(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 = clamp2(0, value, 100);
value /= 100;
} else if (i === 3) {
value = clamp2(0, value, 1);
} else {
value = clamp2(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));
}
static lighten(c, t) {
const oklch = _Color.RGBtoOKLCH(c.r, c.g, c.b);
return _Color.fromOKLCH(clamp2(0, oklch[0] + t, 1), oklch[1], oklch[2]);
}
static darken(c, t) {
const oklch = _Color.RGBtoOKLCH(c.r, c.g, c.b);
return _Color.fromOKLCH(clamp2(0, oklch[0] - t, 1), oklch[1], oklch[2]);
}
static interpolate(colors, count) {
const step = 1 / (colors.length - 1);
const oklchColors = colors.map((c) => _Color.RGBtoOKLCH(c.r, c.g, c.b));
return Array.from({ length: count }, (_, i) => {
const t = i / (count - 1);
const index = colors.length <= 2 ? 0 : Math.min(Math.floor(t * (colors.length - 1)), colors.length - 2);
const q = (t - index * step) / step;
const c0 = oklchColors[index];
const c1 = oklchColors[index + 1];
return _Color.fromOKLCH(lerp(c0[0], c1[0], q), lerp(c0[1], c1[1], q), lerp(c0[2], c1[2], q));
});
}
};
/**
* 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;
}
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 = clamp3(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.defaultTickCount = 0;
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) {
Logger3.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) {
Logger3.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) {
Logger3.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, params) {
const bbox = this.bbox ?? shapeBbox;
if (!bbox.isFinite()) {
return;
}
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, params);
if (gradient == null)
return;
const isOkLch = colorSpace === "oklch";
const step = 0.05;
let c0 = stops[0];
gradient.addColorStop(c0.stop, c0.color);
for (let i = 1; i < stops.length; i += 1) {
const c1 = stops[i];
if (isOkLch) {
const scale = new ColorScale();
scale.domain = [c0.stop, c1.stop];
scale.range = [c0.color, c1.color];
for (let stop = c0.stop + step; stop < c1.stop; stop += step) {
gradient.addColorStop(stop, scale.convert(stop));
}
}
gradient.addColorStop(c1.stop, c1.color);
c0 = c1;
}
if ("createPattern" in gradient) {
gradient = gradient.createPattern();
}
this._cache = { ctx, bbox, gradient };
return gradient;
}
toSvg(shapeBbox) {
const bbox = this.bbox ?? shapeBbox;
const gradient = this.createSvgGradient(bbox);
this.stops.forEach(({ stop: offset, color }) => {
const stop = createSvgElement2("stop");
stop.setAttribute("offset", `${offset}`);
stop.setAttribute("stop-color", `${color}`);
gradient.appendChild(stop);
});
return gradient;
}
};
// packages/ag-charts-community/src/scene/gradient/conicGradient.ts
var ConicGradient = class extends Gradient {
constructor(colorSpace, stops, angle = 0, bbox) {
super(colorSpace, stops, bbox);
this.angle = angle;
}
createCanvasGradient(ctx, bbox, params) {
const angleOffset = -90;
const { angle } = this;
const radians = normalizeAngle360FromDegrees(angle + angleOffset);
const cx = params?.centerX ?? bbox.x + bbox.width * 0.5;
const cy = params?.centerY ?? bbox.y + bbox.height * 0.5;
return ctx.createConicGradient(radians, cx, cy);
}
createSvgGradient(_bbox) {
return createSvgElement3("linearGradient");
}
};
// packages/ag-charts-community/src/scene/gradient/linearGradient.ts
import { createSvgElement as createSvgElement4 } from "ag-charts-core";
var LinearGradient = class extends Gradient {
constructor(colorSpace, stops, angle = 0, bbox) {
super(colorSpace, stops, bbox);
this.angle = angle;
}
getGradientPoints(bbox) {
const angleOffset = 90;
const { angle } = this;
const radians = normalizeAngle360FromDegrees(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 { x0: cx + cos * l, y0: cy + sin * l, x1: cx - cos * l, y1: cy - sin * l };
}
createCanvasGradient(ctx, bbox) {
const { x0, y0, x1, y1 } = this.getGradientPoints(bbox);
if (isNaN(x0) || isNaN(y0) || isNaN(x1) || isNaN(y1)) {
return void 0;
}
return ctx.createLinearGradient(x0, y0, x1, y1);
}
createSvgGradient(bbox) {
const { x0, y0, x1, y1 } = this.getGradientPoints(bbox);
const gradient = createSvgElement4("linearGradient");
gradient.setAttribute("x1", String(x0));
gradient.setAttribute("y1", String(y0));
gradient.setAttribute("x2", String(x1));
gradient.setAttribute("y2", String(y1));
gradient.setAttribute("gradientUnits", "userSpaceOnUse");
return gradient;
}
};
// packages/ag-charts-community/src/scene/gradient/radialGradient.ts
import { createSvgElement as createSvgElement5 } from "ag-charts-core";
var RadialGradient = class extends Gradient {
constructor(colorSpace, stops, bbox) {
super(colorSpace, stops, bbox);
}
createCanvasGradient(ctx, bbox, params) {
const cx = params?.centerX ?? bbox.x + bbox.width * 0.5;
const cy = params?.centerY ?? bbox.y + bbox.height * 0.5;
const innerRadius = params?.innerRadius ?? 0;
const outerRadius = params?.outerRadius ?? Math.hypot(bbox.width * 0.5, bbox.height * 0.5) / Math.SQRT2;
return ctx.createRadialGradient(cx, cy, innerRadius, cx, cy, outerRadius);
}
createSvgGradient(bbox) {
const cx = bbox.x + bbox.width * 0.5;
const cy = bbox.y + bbox.height * 0.5;
const gradient = createSvgElement5("radialGradient");
gradient.setAttribute("cx", String(cx));
gradient.setAttribute("cy", String(cy));
gradient.setAttribute("r", String(Math.hypot(bbox.width * 0.5, bbox.height * 0.5) / Math.SQRT2));
gradient.setAttribute("gradientUnits", "userSpaceOnUse");
return gradient;
}
};
// packages/ag-charts-community/src/scene/gradient/stops.ts
import { Logger as Logger5 } from "ag-charts-core";
// packages/ag-charts-community/src/util/properties.ts
import { Logger as Logger4, isArray as isArray2, isPlainObject as isPlainObject2 } from "ag-charts-core";
var Property = addFakeTransformToInstanceProperty;
var BaseProperties = class {
handleUnknownProperties(_unknownKeys, _properties) {
}
set(properties) {
const { className = this.constructor.name } = this.constructor;
if (properties == null) {
this.clear();
return this;
}
if (typeof properties !== "object") {
Logger4.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) {
Logger4.warn(`unable to set [${String(propertyKey)}] - expecting a properties array`);
} else {
self[propertyKey] = array;
}
} else {
self[propertyKey].set(value);
}
} else if (isPlainObject2(value)) {
self[propertyKey] = merge(value, self[propertyKey] ?? {});
} else {
self[propertyKey] = value;
}
keys.delete(propertyKey);
}
}
this.handleUnknownProperties(keys, properties);
for (const unknownKey of keys) {
Logger4.warn(`unable to set [${String(unknownKey)}] in ${className} - property is unknown`);
}
return this;
}
clear() {
for (const propertyKey of listDecoratedProperties(this)) {
const currentValue = this[propertyKey];
if (isProperties(currentValue)) {
currentValue.clear();
} else {
this[propertyKey] = void 0;
}
}
return this;
}
toJson() {
return listDecoratedProperties(this).reduce((object, propertyKey) => {
const propertyValue = this[propertyKey];
object[String(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 (isArray2(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/scene/gradient/stops.ts
var StopProperties = class extends BaseProperties {
constructor() {
super(...arguments);
this.color = "black";
}
};
__decorateClass([
Property
], StopProperties.prototype, "stop", 2);
__decorateClass([
Property
], StopProperties.prototype, "color", 2);
function stopsAreAscending(fills) {
let currentStop;
for (const fill of fills) {
if (fill?.stop == null)
continue;
if (currentStop != null && fill.stop < currentStop) {
return false;
}
currentStop = fill.stop;
}
return true;
}
function discreteColorStops(colorStops) {
return colorStops.flatMap((colorStop, i) => {
const { stop } = colorStop;
const nextColor = colorStops.at(i + 1)?.color;
return nextColor != null ? [colorStop, { stop, color: nextColor }] : [colorStop];
});
}
function getDefaultColorStops(defaultColorStops, fillMode) {
const stopOffset = fillMode === "discrete" ? 1 : 0;
const colorStops = defaultColorStops.map(
(color, index, { length }) => ({
stop: (index + stopOffset) / (length - 1 + stopOffset),
color
})
);
return fillMode === "discrete" ? discreteColorStops(colorStops) : colorStops;
}
function getColorStops(baseFills, defaultColorStops, domain, fillMode = "continuous") {
const fills = baseFills.map((fill) => typeof fill === "string" ? { color: fill } : fill);
if (fills.length === 0) {
return getDefaultColorStops(defaultColorStops, fillMode);
} else if (!stopsAreAscending(fills)) {
Logger5.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 stops = 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?.stop;
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;
}
stops[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((fill, i) => {
let color = fill?.color;
const stop = stops[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(stop);
}
return { stop, color };
});
return fillMode === "discrete" ? discreteColorStops(colorStops) : colorStops;
}
// packages/ag-charts-community/src/scene/image/image.ts
import { Logger as Logger6, createSvgElement as createSvgElement6 } from "ag-charts-core";
var Image2 = class {
constructor(imageLoader, imageOptions) {
this.imageLoader = imageLoader;
this._cache = void 0;
this.url = imageOptions.url;
this.backgroundFill = imageOptions.backgroundFill ?? "black";
this.backgroundFillOpacity = imageOptions.backgroundFillOpacity ?? 1;
this.repeat = imageOptions.repeat ?? "no-repeat";
this.width = imageOptions.width;
this.height = imageOptions.height;
this.fit = imageOptions.fit ?? "stretch";
this.rotation = imageOptions.rotation ?? 0;
}
createCanvasImage(ctx, image, width, height) {
if (!image)
return null;
const [renderedWidth, renderedHeight] = this.getSize(image.width, image.height, width, height);
if (renderedWidth < 1 || renderedHeight < 1) {
Logger6.warnOnce("Image fill is too small to render, ignoring.");
return null;
}
return ctx.createPattern(image, this.repeat);
}
getSize(imageWidth, imageHeight, width, height) {
const { fit } = this;
let dw = imageWidth;
let dh = imageHeight;
let scale = 1;
const shapeAspectRatio = width / height;
const imageAspectRatio = imageWidth / imageHeight;
if (fit === "stretch" || imageWidth === 0 || imageHeight === 0) {
dw = width;
dh = height;
} else if (fit === "contain") {
scale = imageAspectRatio > shapeAspectRatio ? width / imageWidth : height / imageHeight;
} else if (fit === "cover") {
scale = imageAspectRatio > shapeAspectRatio ? height / imageHeight : width / imageWidth;
}
return [Math.max(1, dw * scale), Math.max(1, dh * scale)];
}
setImageTransform(pattern, bbox) {
if (typeof pattern === "string")
return;
const { url, rotation, width, height } = this;
const image = this.imageLoader?.loadImage(url);
if (!image) {
return;
}
const angle = normalizeAngle360FromDegrees(rotation);
const cos = Math.cos(angle);
const sin = Math.sin(angle);
const [renderedWidth, renderedHeight] = this.getSize(
image.width,
image.height,
width ?? bbox.width,
height ?? bbox.height
);
const widthScale = renderedWidth / image.width;
const heightScale = renderedHeight / image.height;
const bboxCenterX = bbox.x + bbox.width / 2;
const bboxCenterY = bbox.y + bbox.height / 2;
const rotatedW = cos * renderedWidth - sin * renderedHeight;
const rotatedH = sin * renderedWidth + cos * renderedHeight;
const shapeCenterX = rotatedW / 2;
const shapeCenterY = rotatedH / 2;
pattern?.setTransform(
new DOMMatrix([
cos * widthScale,
sin * heightScale,
-sin * widthScale,
cos * heightScale,
bboxCenterX - shapeCenterX,
bboxCenterY - shapeCenterY
])
);
}
createPattern(ctx, shapeWidth, shapeHeight, node) {
const width = this.width ?? shapeWidth;
const height = this.height ?? shapeHeight;
const cache = this._cache;
if (cache != null && cache.ctx === ctx && cache.width === width && cache.height === height) {
return cache.pattern;
}
const image = this.imageLoader?.loadImage(this.url, node);
const pattern = this.createCanvasImage(ctx, image, width, height);
if (pattern == null)
return;
this._cache = { ctx, pattern, width, height };
return pattern;
}
toSvg(bbox, pixelRatio) {
const { url, rotation, backgroundFill, backgroundFillOpacity } = this;
const { x, y, width, height } = bbox;
const pattern = createSvgElement6("pattern");
pattern.setAttribute("viewBox", `0 0 ${width} ${height}`);
pattern.setAttribute("x", String(x));
pattern.setAttribute("y", String(y));
pattern.setAttribute("width", String(width));
pattern.setAttribute("height", String(height));
pattern.setAttribute("patternUnits", "userSpaceOnUse");
const rect = createSvgElement6("rect");
rect.setAttribute("x", "0");
rect.setAttribute("y", "0");
rect.setAttribute("width", String(width));
rect.setAttribute("height", String(height));
rect.setAttribute("fill", backgroundFill);
rect.setAttribute("fill-opacity", String(backgroundFillOpacity));
pattern.appendChild(rect);
const image = createSvgElement6("image");
image.setAttribute("href", url);
image.setAttribute("x", "0");
image.setAttribute("y", "0");
image.setAttribute("width", String(width));
image.setAttribute("height", String(height));
image.setAttribute("preserveAspectRatio", "none");
image.setAttribute("transform", `scale(${1 / pixelRatio}) rotate(${rotation}, ${width / 2}, ${height / 2})`);
pattern.appendChild(image);
return pattern;
}
};
// packages/ag-charts-community/src/scene/pattern/pattern.ts
import {
Logger as Logger8,
createSvgElement as createSvgElement7
} from "ag-charts-core";
// 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));
}
// packages/ag-charts-community/src/util/svg.ts
import { Logger as Logger7 } from "ag-charts-core";
var commandEx = /^[\t\n\f\r ]*([achlmqstvz])[\t\n\f\r ]*/i;
var coordinateEx = /^[+-]?((\d*\.\d+)|(\d+\.)|(\d+))(e[+-]?\d+)?/i;
var commaEx = /[\t\n\f\r ]*,?[\t\n\f\r ]*/;
var flagEx = /^[01]/;
var pathParams = {
z: [],
h: [coordinateEx],
v: [coordinateEx],
m: [coordinateEx, coordinateEx],
l: [coordinateEx, coordinateEx],
t: [coordinateEx, coordinateEx],
s: [coordinateEx, coordinateEx, coordinateEx, coordinateEx],
q: [coordinateEx, coordinateEx, coordinateEx, coordinateEx],
c: [coordinateEx, coordinateEx, coordinateEx, coordinateEx, coordinateEx, coordinateEx],
a: [coordinateEx, coordinateEx, coordinateEx, flagEx, flagEx, coordinateEx, coordinateEx]
};
function parseSvg(d) {
if (!d)
return;
const segments = [];
let i = 0;
let currentCommand;
while (i < d.length) {
const commandMatch = commandEx.exec(d.slice(i));
let command;
if (commandMatch == null) {
if (!currentCommand) {
Logger7.warnOnce(`Invalid SVG path, error at index ${i}: Missing command.`);
return;
}
command = currentCommand;
} else {
command = commandMatch[1];
i += commandMatch[0].length;
}
const segment = parseSegment(command, d, i);
if (!segment)
return;
i = segment[0];
currentCommand = command;
segments.push(segment[1]);
}
return segments;
}
function parseSegment(command, d, index) {
const params = pathParams[command.toLocaleLowerCase()];
const pathSeg = { command, params: [] };
for (const regex of params) {
const segment = d.slice(index);
const match = regex.exec(segment);
if (match != null) {
pathSeg.params.push(parseFloat(match[0]));
index += match[0].length;
const next = commaEx.exec(segment.slice(match[0].length));
if (next != null) {
index += next[0].length;
}
} else if (pathSeg.params.length === 1) {
return [index, pathSeg];
} else {
Logger7.warnOnce(
`Invalid SVG path, error at index ${index}: No path segment parameters for command [${command}]`
);
return;
}
}
return [index, pathSeg];
}
// 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, delta3 = 1e-6) {
if (Math.abs(a) < delta3) {
return linearRoot(b, c);
}
const D = b * b - 4 * a * c;
const roots = [];
if (Math.abs(D) < delta3) {
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, delta3 = 1e-6) {
if (Math.abs(a) < delta3) {
return quadraticRoots(b, c, d, delta3);
}
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 (Math.abs(Im) < delta3) {
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:
// |-1 3 -3 1| |P1|
// [t^3 t^2 t 1] | 3 -6 3 0| |P2|
// |-3 3 0 0| |P3|
// | 1 0 0 0| |P4|
-P1 + 3 * P2 - 3 * P3 + P4,
3 * P1 - 6 * P2 + 3 * P3,
-3 * P1 + 3 * P2,
P1
];
}
// packages/ag-charts-community/src/scene/util/bezier.ts
import { insertListItemsSorted } from "ag-charts-core";
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 splitBezier2D(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y, t) {
const x01 = (1 - t) * p0x + t * p1x;
const y01 = (1 - t) * p0y + t * p1y;
const x12 = (1 - t) * p1x + t * p2x;
const y12 = (1 - t) * p1y + t * p2y;
const x23 = (1 - t) * p2x + t * p3x;
const y23 = (1 - t) * p2y + t * p3y;
const x012 = (1 - t) * x01 + t * x12;
const y012 = (1 - t) * y01 + t * y12;
const x123 = (1 - t) * x12 + t * x23;
const y123 = (1 - t) * y12 + t * y23;
const x0123 = (1 - t) * x012 + t * x123;
const y0123 = (1 - t) * y012 + t * y123;
return [
[
{ x: p0x, y: p0y },
{ x: x01, y: y01 },
{ x: x012, y: y012 },
{ x: x0123, y: y0123 }
],
[
{ x: x0123, y: y0123 },
{ x: x123, y: y123 },
{ x: x23, y: y23 },
{ x: p3x, y: p3y }
]
];
}
function calculateDerivativeExtrema(p0, p1, p2, p3) {
const a = -p0 + 3 * p1 - 3 * p2 + p3;
const b = 2 * (p0 - 2 * p1 + p2);
const c = -p0 + 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 bezier2DExtrema(cp0x, cp0y, cp1x, cp1y, cp2x, cp2y, cp3x, cp3y) {
const tx = calculateDerivativeExtrema(cp0x, cp1x, cp2x, cp3x);
const ty = calculateDerivativeExtrema(cp0y, cp1y, cp2y, cp3y);
return [...tx, ...ty];
}
function bezierCandidate(points, x, y) {
const midX = evaluateBezier(points[0].x, points[1].x, points[2].x, points[3].x, 0.5);
const midY = evaluateBezier(points[0].y, points[1].y, points[2].y, points[3].y, 0.5);
const distance = Math.hypot(midX - x, midY - y);
const minDistance = Math.min(
Math.hypot(points[0].x - x, points[0].y - y),
Math.hypot(points[1].x - x, points[1].y - y),
Math.hypot(points[2].x - x, points[2].y - y),
Math.hypot(points[3].x - x, points[3].y - y)
);
return { points, distance, minDistance };
}
function bezier2DDistance(cp0x, cp0y, cp1x, cp1y, cp2x, cp2y, cp3x, cp3y, x, y, precision = 1) {
const points0 = [
{ x: cp0x, y: cp0y },
{ x: cp1x, y: cp1y },
{ x: cp2x, y: cp2y },
{ x: cp3x, y: cp3y }
];
let queue = {
value: bezierCandidate(points0, x, y),
next: null
};
let bestResult;
while (queue != null) {
const { points, distance, minDistance } = queue.value;
queue = queue.next;
if (bestResult == null || distance < bestResult.distance) {
bestResult = { distance, minDistance };
}
if (bestResult != null && bestResult.distance - minDistance <= precision) {
continue;
}
const [leftPoints, rightPoints] = splitBezier2D(
points[0].x,
points[0].y,
points[1].x,
points[1].y,
points[2].x,
points[2].y,
points[3].x,
points[3].y,
0.5
);
const newCandidates = [bezierCandidate(leftPoints, x, y), bezierCandidate(rightPoints, x, y)].sort(
bezierCandidateCmp
);
queue = insertListItemsSorted(queue, newCandidates, bezierCandidateCmp);
}
return bestResult?.distance ?? Infinity;
}
var bezierCandidateCmp = (a, b) => b.minDistance - a.minDistance;
// 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.cx = NaN;
this.cy = NaN;
this.sx = NaN;
this.sy = NaN;
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.sx = x;
this.sy = y;
this.cx = x;
this.cy = y;
this.path2d.moveTo(x, y);
this.commands.push(0 /* Move */);
this.params.push(x, y);
}
lineTo(x, y) {
if (this.openedPath) {
this.cx = x;
this.cy = y;
this.path2d.lineTo(x, y);
this.commands.push(1 /* Line */);
this.params.push(x, y);
} else {
this.moveTo(x, y);
}
}
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(2 /* Curve */);
this.params.push(cx1, cy1, cx2, cy2, x, y);
}
closePath() {
if (this.openedPath) {
this.cx = this.sx;
this.cy = this.sy;
this.sx = NaN;
this.sy = NaN;
this.path2d.closePath();
this.commands.push(3 /* ClosePath */);
this.openedPath = false;
this.closedPath = true;
}
}
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();
}
ellipse(cx, cy, rx, ry, rotation, sAngle, eAngle, counterClockwise = false) {
const r = rx;
const scaleY = ry / rx;
const mxx = Math.cos(rotation);
const myx = Math.sin(rotation);
const mxy = -scaleY * myx;
const myy = scaleY * mxx;
const x0 = r * Math.cos(sAngle);
const y0 = r * Math.sin(sAngle);
const sx = cx + mxx * x0 + mxy * y0;
const sy = cy + myx * x0 + myy * y0;
const distanceSquared = (sx - this.cx) ** 2 + (sy - this.cy) ** 2;
if (!this.openedPath) {
this.moveTo(sx, sy);
} else if (distanceSquared > 1e-6) {
this.lineTo(sx, sy);
}
let sweep = counterClockwise ? -normalizeAngle360(sAngle - eAngle) : normalizeAngle360(eAngle - sAngle);
if (Math.abs(Math.abs(eAngle - sAngle) - 2 * Math.PI) < 1e-6 && sweep < 2 * Math.PI) {
sweep += 2 * Math.PI * (counterClockwise ? -1 : 1);
}
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);
for (let i = 0; i < arcSections; i += 1) {
const a0 = sAngle + step * (i + 0);
const a1 = sAngle + 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);
const cp1x = rCosStart - h * rSinStart;
const cp1y = rSinStart + h * rCosStart;
const cp2x = rCosEnd + h * rSinEnd;
const cp2y = rSinEnd - h * rCosEnd;
const cp3x = rCosEnd;
const cp3y = rSinEnd;
this.cubicCurveTo(
cx + mxx * cp1x + mxy * cp1y,
cy + myx * cp1x + myy * cp1y,
cx + mxx * cp2x + mxy * cp2y,
cy + myx * cp2x + myy * cp2y,
cx + mxx * cp3x + mxy * cp3y,
cy + myx * cp3x + myy * cp3y
);
}
}
arc(x, y, r, sAngle, eAngle, counterClockwise) {
this.ellipse(x, y, r, r, 0, sAngle, eAngle, counterClockwise);
}
appendSvg(svg) {
const parts = parseSvg(svg);
if (parts == null)
return false;
let sx = 0;
let sy = 0;
let cx;
let cy;
let cpx = 0;
let cpy = 0;
for (const { command, params } of parts) {
cx ?? (cx = params[0]);
cy ?? (cy = params[1]);
const relative = command === command.toLowerCase();
const dx = relative ? cx : 0;
const dy = relative ? cy : 0;
switch (command.toLowerCase()) {
case "m":
this.moveTo(dx + params[0], dy + params[1]);
cx = dx + params[0];
cy = dy + params[1];
sx = cx;
sy = cy;
break;
case "c":
this.cubicCurveTo(
dx + params[0],
dy + params[1],
dx + params[2],
dy + params[3],
dx + params[4],
dy + params[5]
);
cpx = dx + params[2];
cpy = dy + params[3];
cx = dx + params[4];
cy = dy + params[5];
break;
case "s":
this.cubicCurveTo(
cx + cx - cpx,
cy + cy - cpy,
dx + params[0],
dy + params[1],
dx + params[2],
dy + params[3]
);
cpx = dx + params[0];
cpy = dy + params[1];
cx = dx + params[2];
cy = dy + params[3];
break;
case "q":
this.cubicCurveTo(
(dx + 2 * params[0]) / 3,
(dy + 2 * params[1]) / 3,
(2 * params[0] + params[2]) / 3,
(2 * params[1] + params[3]) / 3,
params[2],
params[3]
);
cpx = params[0];
cpy = params[1];
cx = params[2];
cy = params[3];
break;
case "t":
this.cubicCurveTo(
(cx + 2 * (cx + cx - cpx)) / 3,
(cy + 2 * (cy + cy - cpy)) / 3,
(2 * (cx + cx - cpx) + params[0]) / 3,
(2 * (cy + cy - cpy) + params[1]) / 3,
params[0],
params[1]
);
cpx = cx + cx - cpx;
cpy = cy + cy - cpy;
cx = params[0];
cy = params[1];
break;
case "a":
this.svgEllipse(
cx,
cy,
params[0],
params[1],
params[2] * Math.PI / 180,
params[3],
params[4],
dx + params[5],
dy + params[6]
);
cx = dx + params[5];
cy = dy + params[6];
break;
case "h":
this.lineTo(dx + params[0], cy);
cx = dx + params[0];
break;
case "l":
this.lineTo(dx + params[0], dy + params[1]);
cx = dx + params[0];
cy = dy + params[1];
break;
case "v":
this.lineTo(cx, dy + params[0]);
cy = dy + params[0];
break;
case "z":
this.closePath();
cx = sx;
cy = sy;
break;
default:
throw new Error(`Could not translate command '${command}' with '${params.join(" ")}'`);
}
}
return true;
}
svgEllipse(x1, y1, rx, ry, rotation, fA, fS, x2, y2) {
rx = Math.abs(rx);
ry = Math.abs(ry);
const dx = (x1 - x2) / 2;
const dy = (y1 - y2) / 2;
const sin = Math.sin(rotation);
const cos = Math.cos(rotation);
const rotX = cos * dx + sin * dy;
const rotY = -sin * dx + cos * dy;
const normX = rotX / rx;
const normY = rotY / ry;
let scale = normX * normX + normY * normY;
let cx = (x1 + x2) / 2;
let cy = (y1 + y2) / 2;
let cpx = 0;
let cpy = 0;
if (scale >= 1) {
scale = Math.sqrt(scale);
rx *= scale;
ry *= scale;
} else {
scale = Math.sqrt(1 / scale - 1);
if (fA === fS)
scale = -scale;
cpx = scale * rx * normY;
cpy = -scale * ry * normX;
cx += cos * cpx - sin * cpy;
cy += sin * cpx + cos * cpy;
}
const sAngle = Math.atan2((rotY - cpy) / ry, (rotX - cpx) / rx);
const deltaTheta = Math.atan2((-rotY - cpy) / ry, (-rotX - cpx) / rx) - sAngle;
const eAngle = sAngle + deltaTheta;
const counterClockwise = !!(1 - fS);
this.ellipse(cx, cy, rx, ry, rotation, sAngle, eAngle, counterClockwise);
}
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 2 /* 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 3 /* 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 cx = 0;
let cy = 0;
for (let ci = 0, pi = 0; ci < cn; ci++) {
switch (commands[ci]) {
case 0 /* Move */:
cx = sx = params[pi++];
cy = sy = params[pi++];
break;
case 1 /* Line */: {
const x0 = cx;
const y0 = cy;
cx = params[pi++];
cy = params[pi++];
best = lineDistanceSquared(x, y, x0, y0, cx, cy, best);
break;
}
case 2 /* Curve */:
const cp0x = cx;
const cp0y = cy;
const cp1x = params[pi++];
const cp1y = params[pi++];
const cp2x = params[pi++];
const cp2y = params[pi++];
cx = params[pi++];
cy = params[pi++];
best = bezier2DDistance(cp0x, cp0y, cp1x, cp1y, cp2x, cp2y, cx, cy, x, y) ** 2;
break;
case 3 /* ClosePath */:
best = lineDistanceSquared(x, y, cx, cy, 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, count) => {
buffer.push(command);
for (let i = 0; i < count; i += 2) {
const { x, y } = transform(params[pi++], params[pi++]);
buffer.push(x, y);
}
};
let pi = 0;
for (const command of commands) {
switch (command) {
case 0 /* Move */:
addCommand("M", 2);
break;
case 1 /* Line */:
addCommand("L", 2);
break;
case 2 /* Curve */:
addCommand("C", 6);
break;
case 3 /* ClosePath */:
addCommand("Z", 0);
break;
}
}
return buffer.join(" ");
}
computeBBox() {
const { commands, params } = this;
let [top, left, right, bot] = [Infinity, Infinity, -Infinity, -Infinity];
let [cx, cy] = [NaN, NaN];
let [sx, sy] = [NaN, NaN];
const joinPoint = (x, y) => {
top = Math.min(y, top);
left = Math.min(x, left);
right = Math.max(x, right);
bot = Math.max(y, bot);
cx = x;
cy = y;
};
let pi = 0;
for (const command of commands) {
switch (command) {
case 0 /* Move */:
joinPoint(params[pi++], params[pi++]);
sx = cx;
sy = cy;
break;
case 1 /* Line */:
joinPoint(params[pi++], params[pi++]);
break;
case 2 /* Curve */: {
const cp0x = cx;
const cp0y = cy;
const cp1x = params[pi++];
const cp1y = params[pi++];
const cp2x = params[pi++];
const cp2y = params[pi++];
const cp3x = params[pi++];
const cp3y = params[pi++];
const ts = bezier2DExtrema(cp0x, cp0y, cp1x, cp1y, cp2x, cp2y, cp3x, cp3y);
ts.forEach((t) => {
const px = evaluateBezier(cp0x, cp1x, cp2x, cp3x, t);
const py = evaluateBezier(cp0y, cp1y, cp2y, cp3y, t);
joinPoint(px, py);
});
joinPoint(cp3x, cp3y);
break;
}
case 3 /* ClosePath */:
joinPoint(sx, sy);
sx = NaN;
sy = NaN;
break;
}
}
return new BBox(left, top, right - left, bot - top);
}
};
// 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/pattern/patterns.ts
function drawPatternUnitPolygon(path, params, moves) {
const { width, height, padding, strokeWidth } = params;
const x0 = width / 2;
const y0 = height / 2;
const w = Math.max(1, width - padding - strokeWidth / 2);
const h = Math.max(1, height - padding - strokeWidth / 2);
let didMove = false;
for (const [dx, dy] of moves) {
const x = x0 + (dx - 0.5) * w;
const y = y0 + (dy - 0.5) * h;
if (didMove) {
path.lineTo(x, y);
} else {
path.moveTo(x, y);
}
didMove = true;
}
path.closePath();
}
var PATTERNS = {
circles(path, { width, strokeWidth, padding }) {
const c = width / 2;
const r = Math.max(1, c - padding - strokeWidth / 2);
path.arc(c, c, r, 0, Math.PI * 2);
},
squares(path, { width, height, pixelRatio, padding, strokeWidth }) {
const offset = padding + strokeWidth / 2;
path.moveTo(align(pixelRatio, offset), align(pixelRatio, offset));
path.lineTo(align(pixelRatio, width - offset), align(pixelRatio, offset));
path.lineTo(align(pixelRatio, width - offset), align(pixelRatio, height - offset));
path.lineTo(align(pixelRatio, offset), align(pixelRatio, height - offset));
path.closePath();
},
triangles(path, params) {
drawPatternUnitPolygon(path, params, [
[0.5, 0],
[1, 1],
[0, 1]
]);
},
diamonds(path, params) {
drawPatternUnitPolygon(path, params, [
[0.5, 0],
[1, 0.5],
[0.5, 1],
[0, 0.5]
]);
},
stars(path, { width, height, padding }) {
const spikes = 5;
const outerRadius = Math.max(1, (width - padding) / 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 = width / 2 + Math.cos(angle) * radius;
const yCoordinate = height / 2 + Math.sin(angle) * radius;
path.lineTo(xCoordinate, yCoordinate);
}
path.closePath();
},
hearts(path, { width, height, padding }) {
const r = Math.max(1, width / 4 - padding / 2);
const x = width / 2;
const y = height / 2 + r / 2;
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();
},
crosses(path, params) {
drawPatternUnitPolygon(path, 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]
]);
},
"vertical-lines"(path, { width, height, pixelRatio, strokeWidth }) {
const x = align(pixelRatio, width / 2) - strokeWidth % 2 / 2;
path.moveTo(x, 0);
path.lineTo(x, height);
},
"horizontal-lines"(path, { width, height, pixelRatio, strokeWidth }) {
const y = align(pixelRatio, height / 2) - strokeWidth % 2 / 2;
path.moveTo(0, y);
path.lineTo(width, y);
},
"forward-slanted-lines"(path, { width, height, strokeWidth }) {
const angle = Math.atan2(height, width);
const insetX = strokeWidth * Math.cos(angle);
const insetY = strokeWidth * Math.sin(angle);
path.moveTo(-insetX, insetY);
path.lineTo(insetX, -insetY);
path.moveTo(-insetX, height + insetY);
path.lineTo(width + insetX, -insetY);
path.moveTo(width - insetX, height + insetY);
path.lineTo(width + insetX, height - insetY);
},
"backward-slanted-lines"(path, { width, height, strokeWidth }) {
const angle = Math.atan2(height, width);
const insetX = strokeWidth * Math.cos(angle);
const insetY = strokeWidth * Math.sin(angle);
path.moveTo(width - insetX, -insetY);
path.lineTo(width + insetX, insetY);
path.moveTo(-insetX, -insetY);
path.lineTo(width + insetX, height + insetY);
path.moveTo(-insetX, height - insetY);
path.lineTo(insetX, height + insetY);
}
};
// packages/ag-charts-community/src/scene/pattern/pattern.ts
var Pattern = class {
constructor(patternOptions) {
this._cache = void 0;
this.width = Math.max(patternOptions?.width ?? 10, 1);
this.height = Math.max(patternOptions?.height ?? 10, 1);
this.fill = patternOptions.fill ?? "none";
this.fillOpacity = patternOptions.fillOpacity ?? 1;
this.backgroundFill = patternOptions.backgroundFill ?? "none";
this.backgroundFillOpacity = patternOptions.backgroundFillOpacity ?? 1;
this.stroke = patternOptions.stroke ?? "black";
this.strokeOpacity = patternOptions.strokeOpacity ?? 1;
this.strokeWidth = patternOptions.strokeWidth ?? 1;
this.padding = patternOptions.padding ?? 1;
this.pattern = patternOptions.pattern ?? "forward-slanted-lines";
this.rotation = patternOptions.rotation ?? 0;
this.scale = patternOptions.scale ?? 1;
this.path = patternOptions.path;
}
getPath(pixelRatio) {
const { pattern, width, height, padding, strokeWidth, path: svgPath } = this;
const path = new ExtendedPath2D();
let renderPattern = PATTERNS[pattern] != null;
if (svgPath) {
renderPattern && (renderPattern = !path.appendSvg(svgPath));
}
if (renderPattern) {
PATTERNS[pattern](path, { width, height, pixelRatio, strokeWidth, padding });
}
return path;
}
renderStroke(path2d, ctx) {
const { stroke, strokeWidth, strokeOpacity } = this;
if (!strokeWidth)
return;
ctx.strokeStyle = stroke;
ctx.lineWidth = strokeWidth;
ctx.globalAlpha = strokeOpacity;
ctx.stroke(path2d);
}
renderFill(path2d, ctx) {
const { fill, fillOpacity } = this;
if (fill === "none") {
return;
}
ctx.fillStyle = fill;
ctx.globalAlpha = fillOpacity;
ctx.fill(path2d);
}
createCanvasPattern(ctx, pixelRatio) {
const { width, height, scale, backgroundFill, backgroundFillOpacity } = this;
if (width * scale < 1 || height * scale < 1) {
Logger8.warnOnce("Pattern fill is too small to render, ignoring.");
return null;
}
const offscreenPattern = new HdpiOffscreenCanvas({ width, height, pixelRatio: pixelRatio * scale });
const offscreenPatternCtx = offscreenPattern.context;
if (backgroundFill !== "none") {
offscreenPatternCtx.fillStyle = backgroundFill;
offscreenPatternCtx.globalAlpha = backgroundFillOpacity;
offscreenPatternCtx.fillRect(0, 0, width, height);
}
const path2d = this.getPath(pixelRatio).getPath2D();
this.renderFill(path2d, offscreenPatternCtx);
this.renderStroke(path2d, offscreenPatternCtx);
const pattern = ctx.createPattern(offscreenPattern.canvas, "repeat");
this.setPatternTransform(pattern, pixelRatio);
offscreenPattern.destroy();
return pattern;
}
setPatternTransform(pattern, pixelRatio, tx = 0, ty = 0) {
if (pattern == null)
return;
const angle = normalizeAngle360FromDegrees(this.rotation);
const scale = 1 / pixelRatio;
const cos = Math.cos(angle) * scale;
const sin = Math.sin(angle) * scale;
pattern.setTransform(new DOMMatrix([cos, sin, -sin, cos, tx, ty]));
}
createPattern(ctx, pixelRatio) {
if (this._cache != null && this._cache.ctx === ctx && this._cache.pixelRatio === pixelRatio) {
return this._cache.pattern;
}
const pattern = this.createCanvasPattern(ctx, pixelRatio);
if (pattern == null)
return;
this._cache = { ctx, pattern, pixelRatio };
return pattern;
}
toSvg() {
const {
width,
height,
fill,
fillOpacity,
backgroundFill,
backgroundFillOpacity,
stroke,
strokeWidth,
strokeOpacity,
rotation,
scale
} = this;
const pattern = createSvgElement7("pattern");
pattern.setAttribute("viewBox", `0 0 ${width} ${height}`);
pattern.setAttribute("width", String(width));
pattern.setAttribute("height", String(height));
pattern.setAttribute("patternUnits", "userSpaceOnUse");
const rect = createSvgElement7("rect");
rect.setAttribute("x", "0");
rect.setAttribute("y", "0");
rect.setAttribute("width", String(width));
rect.setAttribute("height", String(height));
rect.setAttribute("fill", backgroundFill);
rect.setAttribute("fill-opacity", String(backgroundFillOpacity));
pattern.appendChild(rect);
const path = createSvgElement7("path");
path.setAttribute("fill", fill);
path.setAttribute("fill-opacity", String(fillOpacity));
path.setAttribute("stroke-opacity", String(strokeOpacity));
path.setAttribute("stroke", stroke);
path.setAttribute("stroke-width", String(strokeWidth));
path.setAttribute("transform", `rotate(${rotation}) scale(${scale})`);
path.setAttribute("d", this.getPath(1).toSVG());
pattern.appendChild(path);
return pattern;
}
};
// packages/ag-charts-community/src/scene/util/fill.ts
import { isArray as isArray3, isObject as isObject2 } from "ag-charts-core";
function isGradientFill(fill) {
return isObject2(fill) && fill.type == "gradient";
}
function isPatternFill(fill) {
return fill !== null && isObject2(fill) && fill.type == "pattern";
}
function isImageFill(fill) {
return fill !== null && isObject2(fill) && fill.type == "image";
}
// packages/ag-charts-community/src/scene/shape/svgUtils.ts
function setSvgFontAttributes(element, options) {
const { fontStyle, fontWeight, fontSize, fontFamily } = options;
if (fontStyle)
element.setAttribute("font-style", fontStyle);
if (fontWeight)
element.setAttribute("font-weight", String(fontWeight));
if (fontSize != null)
element.setAttribute("font-size", String(fontSize));
if (fontFamily)
element.setAttribute("font-family", fontFamily);
}
function setSvgStrokeAttributes(element, options) {
const { stroke, strokeWidth, strokeOpacity } = options;
if (stroke)
element.setAttribute("stroke", stroke);
if (strokeWidth != null)
element.setAttribute("stroke-width", String(strokeWidth));
if (strokeOpacity != null)
element.setAttribute("stroke-opacity", String(strokeOpacity));
}
function setSvgLineDashAttributes(element, options) {
const { lineDash, lineDashOffset } = options;
if (lineDash?.some((d) => d !== 0)) {
const lineDashArray = lineDash.length % 2 === 1 ? [...lineDash, ...lineDash] : lineDash;
element.setAttribute("stroke-dasharray", lineDashArray.join(" "));
if (lineDashOffset != null)
element.setAttribute("stroke-dashoffset", String(lineDashOffset));
}
}
// packages/ag-charts-community/src/scene/shape/shape.ts
var Shape = class extends Node {
constructor() {
super(...arguments);
this.fillOpacity = 1;
this.strokeOpacity = 1;
this.fill = "black";
this.strokeWidth = 0;
this.lineDashOffset = 0;
this.opacity = 1;
}
getGradient(fill) {
if (isGradientFill(fill))
return this.createGradient(fill);
}
createGradient(fill) {
const { colorSpace = "rgb", gradient = "linear", colorStops, rotation = 0, reverse = false } = fill;
if (colorStops == null)
return;
let stops = getColorStops(colorStops, ["black"], [0, 1]);
if (reverse) {
stops = stops.map((s) => ({ color: s.color, stop: 1 - s.stop })).reverse();
}
switch (gradient) {
case "linear":
return new LinearGradient(colorSpace, stops, rotation);
case "radial":
return new RadialGradient(colorSpace, stops);
case "conic":
return new ConicGradient(colorSpace, stops, rotation);
}
}
getPattern(fill) {
if (isPatternFill(fill))
return this.createPattern(fill);
}
createPattern(fill) {
return new Pattern(fill);
}
getImage(fill) {
if (isImageFill(fill))
return this.createImage(fill);
}
createImage(fill) {
return new Image2(this.imageLoader, fill);
}
onFillChange() {
if (typeof this.fill === "object") {
if (objectsEqual(this._cachedFill ?? {}, this.fill)) {
return;
}
}
this.fillGradient = this.getGradient(this.fill);
this.fillPattern = this.getPattern(this.fill);
this.fillImage = this.getImage(this.fill);
this._cachedFill = 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);
}
markDirty(property) {
super.markDirty(property);
this.cachedDefaultGradientFillBBox = void 0;
}
fillStroke(ctx, path) {
this.renderFill(ctx, path);
this.renderStroke(ctx, path);
}
renderFill(ctx, path) {
const { fill, fillOpacity, fillImage } = this;
if (fill != null && fill !== "none" && fillOpacity > 0) {
const { globalAlpha } = ctx;
if (fillImage) {
ctx.globalAlpha = fillImage.backgroundFillOpacity;
ctx.fillStyle = fillImage.backgroundFill;
this.executeFill(ctx, path);
ctx.globalAlpha = globalAlpha;
}
this.applyFillAndAlpha(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();
}
}
applyFillAndAlpha(ctx) {
const { fill, fillGradient, fillPattern, fillImage, fillOpacity = 1, opacity = 1 } = this;
ctx.globalAlpha *= opacity * fillOpacity;
if (fillGradient) {
const { fillBBox = this.getDefaultGradientFillBBox() ?? this.getBBox(), fillParams } = this;
ctx.fillStyle = fillGradient.createGradient(ctx, fillBBox, fillParams) ?? "black";
} else if (fillPattern) {
const { x, y } = this.getBBox();
const pixelRatio = this.layerManager?.canvas?.pixelRatio ?? 1;
const pattern = fillPattern.createPattern(ctx, pixelRatio);
fillPattern.setPatternTransform(pattern, pixelRatio, x, y);
if (pattern) {
ctx.fillStyle = pattern;
} else {
ctx.fillStyle = fillPattern.fill;
ctx.globalAlpha *= fillPattern.fillOpacity;
}
} else if (fillImage) {
const bbox = this.getBBox();
const image = fillImage.createPattern(ctx, bbox.width, bbox.height, this);
fillImage.setImageTransform(image, bbox);
ctx.fillStyle = image ?? "transparent";
} else {
ctx.fillStyle = typeof fill === "string" ? fill : "black";
}
}
applyStrokeAndAlpha(ctx) {
const { stroke, strokeOpacity = 1, strokeGradient, opacity = 1 } = this;
ctx.strokeStyle = strokeGradient?.createGradient(ctx, this.getBBox()) ?? (typeof stroke === "string" ? stroke : void 0) ?? "black";
ctx.globalAlpha *= opacity * strokeOpacity;
}
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) {
const { stroke, strokeWidth, strokeOpacity, lineDash, lineDashOffset, lineCap, lineJoin, miterLimit } = this;
if (stroke != null && stroke !== "none" && strokeWidth > 0 && strokeOpacity > 0) {
const { globalAlpha } = ctx;
this.applyStrokeAndAlpha(ctx);
ctx.lineWidth = strokeWidth;
if (lineDash) {
ctx.setLineDash(lineDash);
}
if (lineDashOffset) {
ctx.lineDashOffset = lineDashOffset;
}
if (lineCap) {
ctx.lineCap = lineCap;
}
if (lineJoin) {
ctx.lineJoin = lineJoin;
}
if (miterLimit != null) {
ctx.miterLimit = miterLimit;
}
this.executeStroke(ctx, path);
ctx.globalAlpha = globalAlpha;
}
}
executeStroke(ctx, path) {
if (path) {
ctx.stroke(path);
} else {
ctx.stroke();
}
}
getDefaultGradientFillBBox() {
this.cachedDefaultGradientFillBBox ?? (this.cachedDefaultGradientFillBBox = Object.freeze(this.computeDefaultGradientFillBBox()));
return this.cachedDefaultGradientFillBBox;
}
computeDefaultGradientFillBBox() {
return;
}
containsPoint(x, y) {
return this.isPointInPath(x, y);
}
applySvgFillAttributes(element, defs) {
const { fill, fillOpacity } = this;
if (typeof fill === "string") {
element.setAttribute("fill", fill);
} else if (isGradientFill(fill) && this.fillGradient) {
defs ?? (defs = []);
const gradient = this.fillGradient.toSvg(this.fillBBox ?? this.getBBox());
const id = generateUUID();
gradient.setAttribute("id", id);
defs.push(gradient);
element.setAttribute("fill", `url(#${id})`);
} else if (isPatternFill(fill) && this.fillPattern) {
defs ?? (defs = []);
const pattern = this.fillPattern.toSvg();
const id = generateUUID();
pattern.setAttribute("id", id);
defs.push(pattern);
element.setAttribute("fill", `url(#${id})`);
} else if (isImageFill(fill) && this.fillImage) {
defs ?? (defs = []);
const pixelRatio = this.layerManager?.canvas?.pixelRatio ?? 1;
const pattern = this.fillImage.toSvg(this.getBBox(), pixelRatio);
const id = generateUUID();
pattern.setAttribute("id", id);
defs.push(pattern);
element.setAttribute("fill", `url(#${id})`);
} else {
element.setAttribute("fill", "none");
}
element.setAttribute("fill-opacity", String(fillOpacity));
return defs;
}
applySvgStrokeAttributes(element) {
const { stroke, strokeOpacity, strokeWidth, lineDash, lineDashOffset } = this;
setSvgStrokeAttributes(element, { stroke: isString(stroke) ? stroke : void 0, strokeOpacity, strokeWidth });
setSvgLineDashAttributes(element, { lineDash, lineDashOffset });
}
};
__decorateClass([
SceneChangeDetection()
], Shape.prototype, "fillOpacity", 2);
__decorateClass([
SceneChangeDetection()
], Shape.prototype, "strokeOpacity", 2);
__decorateClass([
SceneObjectChangeDetection({ equals: objectsEqual, changeCb: (s) => s.onFillChange() })
], Shape.prototype, "fill", 2);
__decorateClass([
SceneObjectChangeDetection({ equals: objectsEqual, changeCb: (s) => s.onStrokeChange() })
], Shape.prototype, "stroke", 2);
__decorateClass([
SceneChangeDetection()
], Shape.prototype, "strokeWidth", 2);
__decorateClass([
SceneArrayChangeDetection()
], 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) => clamp4(0, v ?? 1, 1) })
], Shape.prototype, "opacity", 2);
__decorateClass([
SceneObjectChangeDetection({ equals: TRIPLE_EQ, checkDirtyOnAssignment: true })
], Shape.prototype, "fillShadow", 2);
__decorateClass([
SceneObjectChangeDetection({ equals: boxesEqual2, changeCb: (s) => s.onFillChange() })
], Shape.prototype, "fillBBox", 2);
__decorateClass([
SceneObjectChangeDetection({ equals: objectsEqual, changeCb: (s) => s.onFillChange() })
], Shape.prototype, "fillParams", 2);
// packages/ag-charts-community/src/scene/transformable.ts
import { createSvgElement as createSvgElement8 } from "ag-charts-core";
// packages/ag-charts-community/src/scene/matrix.ts
import { isNumberEqual } from "ag-charts-core";
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;
}
onChangeDetection(property) {
super.onChangeDetection(property);
this._dirtyTransform = true;
if (this.batchLevel > 0) {
return;
}
this.markDirty("transform");
}
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) {
({ x, y } = this.fromParentPoint(x, y));
return super.pickNode(x, y);
}
pickNodes(x, y, into) {
({ x, y } = this.fromParentPoint(x, y));
return super.pickNodes(x, y, into);
}
render(renderCtx) {
this.computeTransformMatrix();
const { ctx } = renderCtx;
const matrix = this[TRANSFORM_MATRIX];
let performRestore = false;
try {
if (!matrix.identity) {
ctx.save();
performRestore = true;
matrix.toContext(ctx);
}
super.render(renderCtx);
} finally {
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 = createSvgElement8("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 = 0;
this.rotationCenterY = 0;
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()
], RotatableInternal.prototype, "rotationCenterX", 2);
__decorateClass([
SceneChangeDetection()
], RotatableInternal.prototype, "rotationCenterY", 2);
__decorateClass([
SceneChangeDetection()
], 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 = 0;
this.scalingCenterY = 0;
}
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()
], ScalableInternal.prototype, "scalingX", 2);
__decorateClass([
SceneChangeDetection()
], ScalableInternal.prototype, "scalingY", 2);
__decorateClass([
SceneChangeDetection()
], ScalableInternal.prototype, "scalingCenterX", 2);
__decorateClass([
SceneChangeDetection()
], 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()
], TranslatableInternal.prototype, "translationX", 2);
__decorateClass([
SceneChangeDetection()
], 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/scene/group.ts
var sharedOffscreenCanvas;
var _Group = class _Group extends Node {
// optimizeForInfrequentRedraws: true
constructor(opts) {
super(opts);
this.childNodes = /* @__PURE__ */ new Set();
this.dirty = false;
this.dirtyZIndex = false;
this.clipRect = void 0;
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() !== true)
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;
}
setScene(scene) {
super.setScene(scene);
if (this.layer) {
this.scene?.layersManager.removeLayer(this.layer);
this.layer = void 0;
}
for (const child of this.children()) {
child.setScene(scene);
}
}
markDirty(property) {
this.dirty = true;
super.markDirty(property);
}
markDirtyChildrenOrder() {
super.markDirtyChildrenOrder();
this.dirtyZIndex = true;
this.markDirty();
}
/**
* 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) {
for (const node of toIterable(nodes)) {
node.parentNode?.removeChild(node);
this.childNodes.add(node);
node.parentNode = this;
node.setScene(this.scene);
}
this.markDirtyChildrenOrder();
this.markDirty();
}
appendChild(node) {
this.append(node);
return node;
}
removeChild(node) {
if (!this.childNodes?.delete(node)) {
throw new Error(
`AG Charts - internal error, unknown child node ${node.name ?? node.id} in $${this.name ?? this.id}`
);
}
node.parentNode = void 0;
node.setScene();
this.markDirtyChildrenOrder();
this.markDirty();
}
clear() {
for (const child of this.children()) {
delete child.parentNode;
child.setScene();
}
this.childNodes?.clear();
this.markDirty();
}
/**
* 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) {
if (!this.visible || this.pointerEvents === 1 /* None */ || !this.containsPoint(x, y)) {
return;
}
if (this.childNodes != null && this.childNodes.size !== 0) {
const children = [...this.children()];
for (let i = children.length - 1; i >= 0; i--) {
const child = children[i];
const hit = child.pickNode(x, y);
if (hit != null) {
return hit;
}
}
} else if (!this.isContainerNode) {
return this;
}
}
pickNodes(x, y, into = []) {
if (!this.visible || this.pointerEvents === 1 /* None */ || !this.containsPoint(x, y)) {
return into;
}
if (!this.isContainerNode) {
into.push(this);
}
for (const child of this.children()) {
child.pickNodes(x, y, into);
}
return into;
}
isDirty(renderCtx) {
const { width, height, devicePixelRatio } = renderCtx;
const { dirty, 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;
return dirty || layerResized || pixelRatioChanged;
}
preRender(renderCtx) {
let counts;
if (this.dirty) {
counts = super.preRender(renderCtx, 0);
for (const child of this.children()) {
const childCounts = child.preRender(renderCtx);
counts.groups += childCounts.groups;
counts.nonGroups += childCounts.nonGroups;
counts.complexity += childCounts.complexity;
}
counts.groups += 1;
counts.nonGroups -= 1;
} else {
counts = this.childNodeCounts;
}
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 };
const dirty = this.isDirty(renderCtx);
this.dirty = false;
if (!renderToOffscreenCanvas) {
this.renderInContext(childRenderCtx);
super.render(childRenderCtx);
return;
}
const { ctx, stats, devicePixelRatio: pixelRatio } = renderCtx;
let { image } = this;
if (dirty) {
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();
try {
offscreenCtx.setTransform(...transform);
offscreenCtx.globalAlpha = 1;
this.renderInContext(childRenderCtx);
} finally {
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 };
}
this.image = image;
if (stats)
stats.layersRendered++;
} else if (stats) {
stats.layersSkipped++;
}
const { globalAlpha } = ctx;
ctx.globalAlpha = globalAlpha * this.opacity;
if (layer) {
ctx.save();
try {
ctx.resetTransform();
layer.drawImage(ctx);
} finally {
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);
}
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);
this.dirtyZIndex = false;
}
ctx.save();
try {
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) {
if (stats) {
stats.nodesSkipped += child.childNodeCounts.nonGroups + child.childNodeCounts.groups;
stats.opsSkipped += child.childNodeCounts.complexity;
}
continue;
}
child.isolatedRender(childRenderCtx);
}
} finally {
ctx.restore();
}
}
sortChildren(compareFn) {
if (!this.childNodes)
return;
const sortedChildren = [...this.childNodes].sort(compareFn);
this.childNodes.clear();
for (const child of sortedChildren) {
this.childNodes.add(child);
}
}
*children() {
yield* this.childNodes;
}
*excludeChildren(exclude) {
for (const child of this.children()) {
if (exclude.instance && !(child instanceof exclude.instance) || exclude.name && child.name !== exclude.name) {
yield child;
}
}
}
*descendants() {
for (const child of this.children()) {
yield child;
if (child instanceof _Group) {
yield* child.descendants();
}
}
}
/**
* 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;
}
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) => clamp5(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/sceneDebug.ts
import { Logger as Logger9, getWindow as getWindow2, isString as isString2, toArray as toArray2 } from "ag-charts-core";
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(isString2);
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();
try {
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);
}
} catch (e) {
Logger9.warnOnce("Error during debug stats rendering", e);
} finally {
ctx.restore();
}
}
function prepareSceneNodeHighlight(ctx) {
const config = toArray2(getWindow2("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();
try {
for (const [name, node] of Object.entries(debugNodes)) {
const bbox = Transformable.toCanvas(node);
if (!bbox) {
Logger9.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);
}
} catch (e) {
Logger9.warnOnce("Error during debug rendering", e);
} finally {
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 instanceof Group ? node.dirty : void 0,
...Array.from(node instanceof Group ? 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 ?? "<unknown>"}`,
`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) {
const nodeDirty = node instanceof Group ? node.dirty : void 0;
if (!nodeDirty) {
return { dirtyTree: {}, paths: [] };
}
const childrenDirtyTree = Array.from(node instanceof Group ? 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: nodeDirty,
...childrenDirtyTree.map((c) => c.dirtyTree).filter((t) => t.dirty != null).reduce((result, childTree) => {
result[childTree.name ?? "<unknown>"] = 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/shape/rect.ts
import { boxesEqual as boxesEqual3 } from "ag-charts-core";
// 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/path.ts
import { createSvgElement as createSvgElement9 } from "ag-charts-core";
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("path");
}
}
}
get dirtyPath() {
return this._dirtyPath;
}
checkPathDirty() {
if (this._dirtyPath) {
return;
}
this.dirtyPath = this.path.isDirty() || (this.fillShadow?.isDirty() ?? false) || (this._clipPath?.isDirty() ?? false);
}
resetPathDirty() {
this.path.clear(true);
this._dirtyPath = false;
}
isPathDirty() {
return this.path.isDirty();
}
onChangeDetection(property) {
if (!this._dirtyPath) {
this._dirtyPath = true;
super.onChangeDetection(property);
}
}
computeBBox() {
this.updatePathIfDirty();
return this.path.computeBBox();
}
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();
try {
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);
}
} finally {
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 = createSvgElement9("path");
element.setAttribute("d", this.svgPathData());
const defs = this.applySvgFillAttributes(element, []);
this.applySvgStrokeAttributes(element);
return {
elements: [element],
defs
};
}
};
Path.className = "Path";
__decorateClass([
SceneChangeDetection()
], Path.prototype, "clip", 2);
__decorateClass([
SceneChangeDetection()
], Path.prototype, "clipX", 1);
__decorateClass([
SceneChangeDetection()
], Path.prototype, "clipY", 1);
// 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 = this.strokeWidth;
this.effectiveStrokeWidth = this.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 (w === 0 || h === 0) {
this.effectiveStrokeWidth = 0;
this.lastUpdatePathStrokeWidth = 0;
this.microPixelEffectOpacity = 0;
return;
}
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);
}
applyFillAndAlpha(ctx) {
super.applyFillAndAlpha(ctx);
ctx.globalAlpha *= this.microPixelEffectOpacity;
}
applyStrokeAndAlpha(ctx) {
super.applyStrokeAndAlpha(ctx);
ctx.globalAlpha *= this.microPixelEffectOpacity;
}
renderStroke(ctx) {
const { stroke, effectiveStrokeWidth } = this;
if (stroke && effectiveStrokeWidth) {
const { globalAlpha } = ctx;
const { lineDash, lineDashOffset, lineCap, lineJoin, borderPath, borderClipPath } = this;
if (borderClipPath) {
ctx.clip(borderClipPath.getPath2D());
}
this.applyStrokeAndAlpha(ctx);
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([
SceneChangeDetection()
], Rect.prototype, "x", 2);
__decorateClass([
SceneChangeDetection()
], Rect.prototype, "y", 2);
__decorateClass([
SceneChangeDetection()
], Rect.prototype, "width", 2);
__decorateClass([
SceneChangeDetection()
], Rect.prototype, "height", 2);
__decorateClass([
SceneChangeDetection()
], Rect.prototype, "topLeftCornerRadius", 2);
__decorateClass([
SceneChangeDetection()
], Rect.prototype, "topRightCornerRadius", 2);
__decorateClass([
SceneChangeDetection()
], Rect.prototype, "bottomRightCornerRadius", 2);
__decorateClass([
SceneChangeDetection()
], Rect.prototype, "bottomLeftCornerRadius", 2);
__decorateClass([
SceneObjectChangeDetection({ equals: boxesEqual3 })
], Rect.prototype, "clipBBox", 2);
__decorateClass([
SceneChangeDetection()
], Rect.prototype, "crisp", 2);
// packages/ag-charts-community/src/scene/shape/text.ts
var externUseGlyphIndependentMeasurements = false;
var _Text = class _Text extends Shape {
constructor(options) {
super(options);
this.x = 0;
this.y = 0;
this.lines = [];
this.text = void 0;
this.fontSize = _Text.defaultFontSize;
this.fontFamily = "sans-serif";
this.textAlign = "start";
this.textBaseline = "alphabetic";
this.boxPadding = 0;
this.trimText = options?.trimText ?? true;
}
onTextChange() {
this.richText?.clear();
this.textMap?.clear();
if (isArray4(this.text)) {
this.lines = [];
this.richText ?? (this.richText = new Group());
this.richText.setScene(this.scene);
this.richText.append(this.text.map(() => new _Text({ trimText: false })));
} else {
const lines = this.text?.split("\n") ?? [];
this.lines = this.trimText ? lines.map((line) => line.trim()) : lines;
}
}
static computeBBox(lines, x, y, opts, useGlyphIndependentMeasurements = true) {
const {
font,
font: { fontSize },
textAlign,
textBaseline,
lineHeight = useGlyphIndependentMeasurements ? TextUtils.getLineHeight(fontSize) : void 0
} = opts;
const {
width,
alphabeticBaseline,
offsetLeft: exactOffsetLeft,
offsetTop: exactOffsetTop,
height: exactHeight
} = CachedTextMeasurerPool.measureLines(
lines,
useGlyphIndependentMeasurements ? { font, lineHeight, textAlign: "start", textBaseline: "top" } : opts
);
const height = lineHeight == null ? exactHeight : lineHeight * lines.length;
let offsetTop;
if (lineHeight == null) {
offsetTop = exactOffsetTop;
} else if (textBaseline === "alphabetic") {
const padding = (lineHeight - fontSize) / 2;
offsetTop = padding - alphabeticBaseline;
} else {
offsetTop = TextUtils.getVerticalModifier(textBaseline) * height;
}
const offsetLeft = useGlyphIndependentMeasurements ? width * TextUtils.getHorizontalModifier(textAlign) : exactOffsetLeft;
return new BBox(x - offsetLeft, y - offsetTop, width, height);
}
computeBBox(useGlyphIndependentMeasurements = externUseGlyphIndependentMeasurements) {
this.generateTextMap();
if (this.textMap?.size) {
const bbox = BBox.merge(this.textMap.values());
bbox.x = this.x;
bbox.y = this.y;
return bbox;
}
const { x, y, lines, textBaseline, textAlign, lineHeight } = this;
const measuredTextBounds = _Text.computeBBox(
lines,
x,
y,
{ font: this, textBaseline, textAlign, lineHeight },
useGlyphIndependentMeasurements
);
if (this.boxing != null)
measuredTextBounds.grow(this.boxPadding);
return measuredTextBounds;
}
getBBox(useGlyphIndependentMeasurements = true) {
if (useGlyphIndependentMeasurements) {
externUseGlyphIndependentMeasurements = true;
const bbox = this.computeBBox(true);
externUseGlyphIndependentMeasurements = false;
return bbox;
}
return super.getBBox();
}
getPlainText() {
return toPlainText(this.text);
}
getTextMeasureBBox() {
return this.computeBBox();
}
isPointInPath(x, y) {
return this.getBBox()?.containsPoint(x, y) ?? false;
}
setScene(scene) {
this.richText?.setScene(scene);
super.setScene(scene);
}
generateTextMap() {
if (!isArray4(this.text) || this.textMap?.size)
return;
this.textMap ?? (this.textMap = /* @__PURE__ */ new Map());
let index = 0;
let totalWidth = 0;
let offsetY = 0;
const mainStyle = {
fill: this.fill,
fontSize: this.fontSize,
fontFamily: this.fontFamily,
fontStyle: this.fontStyle,
fontWeight: this.fontWeight
};
for (const textNode of this.richText.children()) {
const { color, ...textSegment } = this.text[index++];
textNode.x = 0;
textNode.y = 0;
textNode.setProperties(mergeDefaults({ fill: color }, textSegment, mainStyle));
const textBBox = textNode.getBBox();
this.textMap.set(textNode, textBBox);
offsetY = Math.max(
offsetY,
textBBox.y + textBBox.height / 2 + (textNode.lineHeight ?? TextUtils.getLineHeight(textNode.fontSize))
);
totalWidth += textBBox.x + textBBox.width;
}
let offsetX = this.x - totalWidth / 2;
for (const [textNode, bbox] of this.textMap) {
textNode.x += offsetX;
textNode.y += offsetY;
offsetX += bbox.width;
}
}
render(renderCtx) {
const { ctx, stats } = renderCtx;
if (!this.text || !this.layerManager) {
if (stats)
stats.nodesSkipped += 1;
return super.render(renderCtx);
}
if (isArray4(this.text)) {
this.generateTextMap();
const { width } = this.richText.getBBox();
let translateX = 0;
switch (this.textAlign) {
case "left":
case "start":
translateX = width / 2;
break;
case "right":
case "end":
translateX = width / -2;
}
ctx.save();
ctx.translate(translateX, this.y);
this.richText.render(renderCtx);
ctx.restore();
} else {
this.renderText(renderCtx);
}
if (_Text.debug.check() && !this.textMap?.size) {
const bbox = this.getBBox(true);
ctx.strokeStyle = "red";
ctx.lineWidth = 1;
ctx.strokeRect(bbox.x, bbox.y, bbox.width, bbox.height);
}
super.render(renderCtx);
}
markDirty(property) {
this.textMap?.clear();
return super.markDirty(property);
}
renderText(renderCtx) {
const { fill, stroke, strokeWidth } = this;
if (!fill && !(stroke && strokeWidth) || !this.layerManager) {
return super.render(renderCtx);
}
const { ctx } = renderCtx;
const font = TextUtils.toFontString(this);
if (ctx.font !== font) {
ctx.font = font;
}
const { fontSize, lineHeight = TextUtils.getLineHeight(fontSize), textAlign, textBaseline } = this;
ctx.textAlign = textAlign;
ctx.textBaseline = textBaseline;
if (this.boxing) {
const opts = { font: this, textBaseline, textAlign, lineHeight };
const textBBox = _Text.computeBBox(this.lines, this.x, this.y, opts);
if (textBBox.width !== 0 && textBBox.height !== 0) {
const { x, y, width, height } = textBBox.grow(this.boxPadding);
this.boxing.opacity = this.opacity;
this.boxing.x = x;
this.boxing.y = y;
this.boxing.width = width;
this.boxing.height = height;
this.boxing.preRender(renderCtx);
this.boxing.render(renderCtx);
}
}
this.fillStroke(ctx);
}
executeFill(ctx) {
const { fontSize, lineHeight = TextUtils.getLineHeight(fontSize), textBaseline, lines } = this;
const lineOriginY = textBaseline === "alphabetic" ? 0 : TextUtils.getVerticalModifier(textBaseline) * lineHeight * (1 - lines.length);
this.renderLines(lineOriginY, lineHeight, (line, x, y) => ctx.fillText(line, x, y));
}
executeStroke(ctx) {
const { fontSize, lineHeight = TextUtils.getLineHeight(fontSize), textBaseline, lines } = this;
const lineOriginY = textBaseline === "alphabetic" ? 0 : TextUtils.getVerticalModifier(textBaseline) * lineHeight * (1 - lines.length);
this.renderLines(lineOriginY, lineHeight, (line, x, y) => ctx.strokeText(line, x, y));
}
renderLines(offsetY, lineHeight, renderCallback) {
const { lines, x, y } = this;
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(offsetY) || !Number.isFinite(lineHeight)) {
return;
}
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;
}
setBoxing(props) {
const stroke = props.border?.enabled ? props.border?.stroke : void 0;
if (props.fill != null || stroke != null) {
this.boxing ?? (this.boxing = new Rect({ scene: this.scene }));
this.boxing.fill = props.fill;
this.boxing.fillOpacity = props.fillOpacity ?? 1;
this.boxing.cornerRadius = props.cornerRadius ?? 0;
this.boxing.stroke = stroke;
this.boxing.strokeWidth = props.border?.strokeWidth ?? 0;
this.boxing.strokeOpacity = props.border?.strokeOpacity ?? 1;
this.boxPadding = props.padding ?? 0;
} else if (this.boxing) {
this.boxing.destroy();
this.boxing = void 0;
}
}
getBoxingProperties() {
const { fill, fillOpacity, cornerRadius, stroke, strokeWidth, strokeOpacity } = this.boxing ?? {};
return {
border: { enabled: stroke != null, stroke, strokeWidth, strokeOpacity },
cornerRadius,
fill,
fillOpacity,
padding: this.boxPadding
};
}
toSVG() {
if (!this.visible || !this.text)
return;
const element = createSvgElement10("text");
if (isString3(this.text)) {
this.applySvgFillAttributes(element);
setSvgFontAttributes(element, this);
element.setAttribute(
"text-anchor",
{
center: "middle",
left: "start",
right: "end",
start: "start",
end: "end"
}[this.textAlign ?? "start"]
);
element.setAttribute("alignment-baseline", this.textBaseline);
element.setAttribute("x", String(this.x));
element.setAttribute("y", String(this.y));
element.textContent = this.text;
} else {
for (const segment of this.text) {
const segmentElement = createSvgElement10("tspan");
setSvgFontAttributes(segmentElement, {
fontSize: segment.fontSize ?? this.fontSize,
fontFamily: segment.fontFamily ?? this.fontFamily,
fontWeight: segment.fontWeight ?? this.fontWeight,
fontStyle: segment.fontStyle ?? this.fontStyle
});
this.applySvgFillAttributes(segmentElement);
segmentElement.textContent = segment.text;
element.append(segmentElement);
}
}
return { elements: [element] };
}
};
_Text.className = "Text";
_Text.debug = Debug.create(true, "scene:text" /* SCENE_TEXT */);
_Text.defaultFontSize = 10;
__decorateClass([
SceneChangeDetection()
], _Text.prototype, "x", 2);
__decorateClass([
SceneChangeDetection()
], _Text.prototype, "y", 2);
__decorateClass([
SceneRefChangeDetection({
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
import { isArray as isArray5 } from "ag-charts-core";
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 = createId2(this);
this.node = new RotatableText({ zIndex: 1 }).setProperties({
textAlign: "center",
pointerEvents: 1 /* None */
});
this.enabled = false;
this.textAlign = "center";
this.fontSize = 10 /* SMALLER */;
this.fontFamily = "sans-serif";
this.wrapping = "always";
this.padding = 0;
this.layoutStyle = "block";
this.truncated = false;
}
registerInteraction(moduleCtx, where) {
return moduleCtx.eventsHub.on("layout:complete", () => this.updateA11yText(moduleCtx, where));
}
computeTextWrap(containerWidth, containerHeight) {
const { text, padding, wrapping } = this;
if (isArray6(text))
return;
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 = toPlainText2(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.truncated) {
const { x, y } = Transformable.toCanvas(this.node);
const canvasX = event.sourceEvent.offsetX + x;
const canvasY = event.sourceEvent.offsetY + y;
moduleCtx.tooltipManager.updateTooltip(this.id, { canvasX, canvasY, showArrow: false }, [
{ type: "structured", title: toPlainText2(this.text) }
]);
}
}
handleMouseLeave(moduleCtx, _event) {
moduleCtx.tooltipManager.removeTooltip(this.id);
}
};
Caption.SMALL_PADDING = 10;
__decorateClass([
Property,
ProxyPropertyOnWrite("node", "visible")
], Caption.prototype, "enabled", 2);
__decorateClass([
Property,
ProxyPropertyOnWrite("node")
], Caption.prototype, "text", 2);
__decorateClass([
Property,
ProxyPropertyOnWrite("node")
], Caption.prototype, "textAlign", 2);
__decorateClass([
Property,
ProxyPropertyOnWrite("node")
], Caption.prototype, "fontStyle", 2);
__decorateClass([
Property,
ProxyPropertyOnWrite("node")
], Caption.prototype, "fontWeight", 2);
__decorateClass([
Property,
ProxyPropertyOnWrite("node")
], Caption.prototype, "fontSize", 2);
__decorateClass([
Property,
ProxyPropertyOnWrite("node")
], Caption.prototype, "fontFamily", 2);
__decorateClass([
Property,
ProxyPropertyOnWrite("node", "fill")
], Caption.prototype, "color", 2);
__decorateClass([
Property
], Caption.prototype, "spacing", 2);
__decorateClass([
Property
], Caption.prototype, "maxWidth", 2);
__decorateClass([
Property
], Caption.prototype, "maxHeight", 2);
__decorateClass([
Property
], Caption.prototype, "wrapping", 2);
__decorateClass([
Property
], Caption.prototype, "padding", 2);
__decorateClass([
Property
], Caption.prototype, "layoutStyle", 2);
// 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.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.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.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;
}
isPointInPath(x, y) {
return this.distanceSquared(x, y) <= 0;
}
distanceSquared(x, y) {
const anchor = Marker.anchor(this.shape);
return Math.max(
Math.hypot(x - this.x + (anchor.x - 0.5) * this.size, y - this.y + (anchor.y - 0.5) * this.size) - this.size / 2,
0
);
}
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([
SceneObjectChangeDetection({ equals: TRIPLE_EQ })
], InternalMarker.prototype, "shape", 2);
__decorateClass([
SceneChangeDetection()
], InternalMarker.prototype, "x", 2);
__decorateClass([
SceneChangeDetection()
], InternalMarker.prototype, "y", 2);
__decorateClass([
SceneChangeDetection({ 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/scale/categoryScale.ts
import { clamp as clamp9 } from "ag-charts-core";
// packages/ag-charts-community/src/util/number.ts
import { clamp as clamp6 } from "ag-charts-core";
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 previousPowerOf2(value) {
value = value | 0;
if (value <= 0)
return 0;
if (value === 1)
return 1;
return 1 << 31 - Math.clz32(value);
}
// 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/encoding.ts
var tzOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset() * durationMinute;
var unitEncoding = {
millisecond: {
milliseconds: 1,
hierarchy: "day",
encode(date) {
return date.getTime();
},
decode(encoded) {
return new Date(encoded);
}
},
second: {
milliseconds: durationSecond,
hierarchy: "day",
encode(date, utc) {
const offset = utc ? 0 : tzOffset;
return Math.floor((date.getTime() - offset) / durationSecond);
},
decode(encoded, utc) {
const offset = utc ? 0 : tzOffset;
return new Date(offset + encoded * durationSecond);
}
},
minute: {
milliseconds: durationMinute,
hierarchy: "day",
encode(date, utc) {
const offset = utc ? 0 : tzOffset;
return Math.floor((date.getTime() - offset) / durationMinute);
},
decode(encoded, utc) {
const offset = utc ? 0 : tzOffset;
return new Date(offset + encoded * durationMinute);
}
},
hour: {
milliseconds: durationHour,
hierarchy: "day",
encode(date, utc) {
const offset = utc ? 0 : tzOffset;
return Math.floor((date.getTime() - offset) / durationHour);
},
decode(encoded, utc) {
const offset = utc ? 0 : tzOffset;
return new Date(offset + encoded * durationHour);
}
},
day: {
milliseconds: durationDay,
hierarchy: "month",
encode(date, utc) {
const tzOffsetMs = utc ? 0 : date.getTimezoneOffset() * durationMinute;
return Math.floor((date.getTime() - tzOffsetMs) / durationDay);
},
decode(encoded, utc) {
let d;
if (utc) {
d = /* @__PURE__ */ new Date(0);
d.setUTCDate(d.getUTCDate() + encoded);
d.setUTCHours(0, 0, 0, 0);
} else {
d = new Date(1970, 0, 1);
d.setDate(d.getDate() + encoded);
}
return d;
}
},
month: {
milliseconds: durationMonth,
hierarchy: "year",
encode(date, utc) {
if (utc) {
return date.getUTCFullYear() * 12 + date.getUTCMonth();
} else {
return date.getFullYear() * 12 + date.getMonth();
}
},
decode(encoded, utc) {
if (utc) {
const year = Math.floor(encoded / 12);
const m = encoded - year * 12;
return new Date(Date.UTC(year, m, 1));
} else {
const y = Math.floor(encoded / 12);
const month = encoded - y * 12;
return new Date(y, month, 1);
}
}
},
year: {
milliseconds: durationYear,
hierarchy: void 0,
encode(date, utc) {
if (utc) {
return date.getUTCFullYear();
} else {
return date.getFullYear();
}
},
decode(encoded, utc) {
let d;
if (utc) {
d = /* @__PURE__ */ new Date();
d.setUTCFullYear(encoded);
d.setUTCMonth(0, 1);
d.setUTCHours(0, 0, 0, 0);
} else {
d = new Date(encoded, 0, 1, 0, 0, 0, 0);
}
return d;
}
}
};
// packages/ag-charts-community/src/util/time/index.ts
function intervalUnit(interval) {
return typeof interval === "string" ? interval : interval.unit;
}
function intervalStep(interval) {
return typeof interval === "string" ? 1 : interval.step ?? 1;
}
function intervalMilliseconds(interval) {
const step = intervalStep(interval);
return step * unitEncoding[intervalUnit(interval)].milliseconds;
}
// packages/ag-charts-community/src/util/timeFormatDefaults.ts
function dateToNumber(value) {
return value instanceof Date ? value.getTime() : value;
}
// packages/ag-charts-community/src/scale/bandScale.ts
import { Logger as Logger10, clamp as clamp7 } from "ag-charts-core";
var _BandScale = class _BandScale extends AbstractScale {
constructor() {
super(...arguments);
this.invalid = true;
this.range = [0, 1];
this.round = false;
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 = clamp7(0, value, 1);
this._paddingInner = value;
this._paddingOuter = value;
}
get padding() {
return this._paddingInner;
}
set paddingInner(value) {
this.invalid = true;
this._paddingInner = clamp7(0, value, 1);
}
get paddingInner() {
return this._paddingInner;
}
set paddingOuter(value) {
this.invalid = true;
this._paddingOuter = clamp7(0, value, 1);
}
get paddingOuter() {
return this._paddingOuter;
}
refresh() {
if (!this.invalid)
return;
this.invalid = false;
this.update();
if (this.invalid) {
Logger10.warnOnce("Expected update to not invalidate scale");
}
}
convert(d, options) {
this.refresh();
const i = this.findIndex(d, options?.alignment);
if (i == null || i < 0 || i >= this.bands.length) {
return NaN;
}
return this.ordinalRange(i);
}
invertNearestIndex(position) {
this.refresh();
const bandCount = this.bands.length;
if (bandCount === 0)
return -1;
let low = 0;
let high = bandCount - 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 [r0, r1] = this.range;
let { _paddingInner: paddingInner } = this;
const { _paddingOuter: paddingOuter, round, bands } = this;
const bandCount = bands.length;
if (bandCount === 0)
return;
const rangeDistance = r1 - r0;
let rawStep;
if (bandCount === 1) {
paddingInner = 0;
rawStep = rangeDistance * (1 - paddingOuter * 2);
} else {
rawStep = rangeDistance / Math.max(1, bandCount - paddingInner + paddingOuter * 2);
}
const step = round ? Math.floor(rawStep) : rawStep;
let inset = r0 + (rangeDistance - step * (bandCount - 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 clamp7(min, inset + step * i, max);
}
};
__decorateClass([
Invalidating
], _BandScale.prototype, "range", 2);
__decorateClass([
Invalidating
], _BandScale.prototype, "round", 2);
var BandScale = _BandScale;
// packages/ag-charts-community/src/scale/scaleUtil.ts
import { clamp as clamp8 } from "ag-charts-core";
function filterVisibleTicks(ticks, reversed, visibleRange) {
if (visibleRange == null || visibleRange[0] === 0 && visibleRange[1] === 1) {
return { ticks, count: ticks.length, firstTickIndex: 0 };
}
const vt0 = clamp8(0, Math.floor(visibleRange[0] * ticks.length), ticks.length);
const vt1 = clamp8(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: ticks.slice(t0, t1),
count: ticks.length,
firstTickIndex: t0
};
}
// packages/ag-charts-community/src/scale/categoryScale.ts
var CategoryScale = class _CategoryScale extends BandScale {
constructor() {
super(...arguments);
this.type = "category";
this.defaultTickCount = 0;
/**
* Maps datum to its index in the {@link domain} array.
* Used to check for duplicate data (not allowed).
*/
this.index = /* @__PURE__ */ new Map();
this.indexInitialized = false;
/**
* Contains unique data only.
*/
this._domain = [];
}
static is(value) {
return value instanceof _CategoryScale;
}
set domain(values) {
if (this._domain === values)
return;
this.invalid = true;
this._domain = values;
this.index.clear();
this.indexInitialized = false;
}
get domain() {
return this._domain;
}
get bands() {
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 offset = nearest ? this.bandwidth / 2 : 0;
const index = this.invertNearestIndex(Math.max(0, position - offset));
const matches = nearest || position === this.ordinalRange(index);
return matches ? this.domain[index] : void 0;
}
ticks(params, domain = this.domain, visibleRange) {
const { bands } = this;
let { tickCount } = params;
if (tickCount === 0) {
const firstTickIndex2 = bands.length > 1 ? 1 : 0;
return { ticks: [bands[firstTickIndex2]], count: void 0, firstTickIndex: firstTickIndex2 };
}
let step = tickCount != null && tickCount !== 0 ? bands.length / tickCount | 0 : 1;
step = previousPowerOf2(step);
if (step <= 1) {
return filterVisibleTicks(domain, false, visibleRange);
}
tickCount = bands.length / step | 0;
const span = step * tickCount;
const inset = previousPowerOf2((bands.length - span) / 2 | 0);
const vt0 = clamp9(0, Math.floor((visibleRange?.[0] ?? 0) * bands.length), bands.length);
const vt1 = clamp9(0, Math.ceil((visibleRange?.[1] ?? 1) * bands.length), bands.length);
const i0 = Math.floor((vt0 - inset) / step) * step + inset;
const i1 = Math.ceil((vt1 - inset) / step) * step + inset;
const ticks = [];
for (let i = i0; i < i1; i += step) {
if (i >= 0 && i < bands.length) {
ticks.push(bands[i]);
}
}
let firstTickIndex = ticks.length > 0 ? this.findIndex(ticks[0]) : void 0;
if (firstTickIndex != null) {
firstTickIndex = Math.floor((firstTickIndex - inset) / step);
}
return { ticks, count: void 0, firstTickIndex };
}
findIndex(value) {
const { index, indexInitialized } = this;
if (!indexInitialized) {
const { domain } = this;
for (let i = 0; i < domain.length; i++) {
index.set(dateToNumber(domain[i]), i);
}
this.indexInitialized = true;
}
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/ticks.ts
import { Logger as Logger11, clamp as clamp10, countFractionDigits, createNumberFormatter, parseNumberFormat } from "ag-charts-core";
var tInterval = (timeInterval, step) => ({
duration: intervalMilliseconds(timeInterval) * step,
timeInterval,
step
});
var TickIntervals = [
tInterval({ unit: "second" }, 1),
tInterval({ unit: "second" }, 5),
tInterval({ unit: "second" }, 15),
tInterval({ unit: "second" }, 30),
tInterval({ unit: "minute" }, 1),
tInterval({ unit: "minute" }, 5),
tInterval({ unit: "minute" }, 15),
tInterval({ unit: "minute" }, 30),
tInterval({ unit: "hour" }, 1),
tInterval({ unit: "hour" }, 3),
tInterval({ unit: "hour" }, 6),
tInterval({ unit: "hour" }, 12),
tInterval({ unit: "day" }, 1),
tInterval({ unit: "day" }, 2),
tInterval({ unit: "day", step: 7 }, 1),
tInterval({ unit: "day", step: 7 }, 2),
tInterval({ unit: "day", step: 7 }, 3),
tInterval({ unit: "month" }, 1),
tInterval({ unit: "month" }, 2),
tInterval({ unit: "month" }, 3),
tInterval({ unit: "month" }, 4),
tInterval({ unit: "month" }, 6),
tInterval({ unit: "year" }, 1)
];
var TickMultipliers = [1, 2, 5, 10];
function isCloseToInteger(n, delta3) {
return Math.abs(Math.round(n) - n) < delta3;
}
function countTicks(d0, d1, step) {
const extent = Math.abs(d1 - d0);
return extent >= step ? Math.abs(d1 - d0) / step + 1 : 1;
}
function createTicks(start, stop, count, minCount, maxCount, visibleRange) {
if (start === stop)
return { ticks: [start], count: 1, firstTickIndex: 0 };
if (count < 2)
return { ticks: [start, stop], count: 2, firstTickIndex: 0 };
const step = tickStep(start, stop, count, minCount, maxCount);
if (!Number.isFinite(step))
return { ticks: [], count: 0, firstTickIndex: void 0 };
let d0 = start;
let d1 = stop;
if (!isCloseToInteger(d0 / step, 1e-12)) {
d0 = Math.ceil(d0 / step) * step;
}
if (!isCloseToInteger(d1 / step, 1e-12)) {
d1 = Math.floor(d1 / step) * step;
}
if (visibleRange != null) {
const dr = d1 - d0;
const vr = stop - start;
const vd0 = start + vr * visibleRange[0];
const vd1 = start + vr * visibleRange[1];
visibleRange = [(vd0 - d0) / dr, (vd1 - d0) / dr];
}
const { ticks } = range(d0, d1, step, visibleRange);
const firstTick = ticks.at(0);
return {
ticks,
count: countTicks(d0, d1, step),
firstTickIndex: firstTick == null ? void 0 : Math.round((firstTick - d0) / step)
};
}
var minPrimaryTickRatio = Math.floor(2 * durationWeek / durationMonth * 10) / 10;
function tickStep(start, end, count, minCount = 0, maxCount = Infinity) {
if (start === end) {
return clamp10(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 range(start, end, step, visibleRange) {
if (!Number.isFinite(step) || step <= 0) {
return { ticks: [], count: 0, firstTickIndex: void 0 };
} else if (start === end) {
return { ticks: [start], count: 1, firstTickIndex: 0 };
}
const f = 10 ** countFractionDigits(step);
const d0 = Math.min(start, end);
const d1 = Math.max(start, end);
let vd0;
let vd1;
if (visibleRange != null && (visibleRange[0] !== 0 || visibleRange[1] !== 1)) {
const rangeExtent = end - start;
const adjustedStart = start + rangeExtent * visibleRange[0];
const adjustedEnd = end - rangeExtent * (1 - visibleRange[1]);
vd0 = Math.min(adjustedStart, adjustedEnd);
vd1 = Math.max(adjustedStart, adjustedEnd);
} else {
vd0 = d0;
vd1 = d1;
}
vd0 = Math.floor(vd0 * f) / f;
vd1 = Math.ceil(vd1 * f) / f;
const ticks = [];
for (let i = 0; ; i += 1) {
const p = Math.round((d0 + step * i) * f) / f;
if (p > d1)
break;
if (p >= vd0 && p <= vd1) {
ticks.push(p);
}
}
const firstTick = ticks.at(0);
return {
ticks,
count: countTicks(d0, d1, step),
firstTickIndex: firstTick == null ? void 0 : Math.round((firstTick - d0) / step)
};
}
function isDenseInterval(count, availableRange) {
if (count >= availableRange) {
Logger11.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 error = 1 - extent / Math.abs(d1 - d0);
if (minError > error) {
minError = error;
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.defaultTickCount = _ContinuousScale.defaultTickCount;
this.defaultClamp = false;
}
static is(value) {
return value instanceof _ContinuousScale;
}
normalizeDomains(...domains) {
return normalizeContinuousDomains(...domains);
}
calcBandwidth(smallestInterval = 1, minWidth = 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;
let bands = intervals;
if (minWidth !== 0) {
const maxBands = Math.floor(rangeDistance);
bands = Math.min(bands, maxBands);
}
return rangeDistance / Math.max(1, bands);
}
convert(value, options) {
const { domain } = this;
if (!domain || domain.length < 2 || value == null) {
return NaN;
}
const { range: range2 } = this;
const clamp11 = options?.clamp ?? this.defaultClamp;
let d0 = domain[0].valueOf();
let d1 = domain[1].valueOf();
let x = value.valueOf();
if (this.transform) {
d0 = this.transform(d0);
d1 = this.transform(d1);
x = this.transform(x);
}
if (clamp11) {
const [start, stop] = findMinMax([d0, d1]);
if (x < start) {
return range2[0];
} else if (x > stop) {
return range2[1];
}
}
if (d0 === d1) {
return (range2[0] + range2[1]) / 2;
} else if (x === d0) {
return range2[0];
} else if (x === d1) {
return range2[1];
}
const r0 = range2[0];
return r0 + (x - d0) / (d1 - d0) * (range2[1] - r0);
}
invert(x, _nearest) {
const { domain } = this;
if (domain.length < 2)
return;
let d0 = domain[0].valueOf();
let d1 = domain[1].valueOf();
if (this.transform) {
d0 = this.transform(d0);
d1 = this.transform(d1);
}
const { range: range2 } = this;
const [r0, r1] = range2;
let d;
if (r0 === r1) {
d = this.toDomain((d0 + d1) / 2);
} else {
d = this.toDomain(d0 + (x - r0) / (r1 - r0) * (d1 - d0));
}
return this.transformInvert ? this.transformInvert(d) : d;
}
getPixelRange() {
const [a, b] = this.range;
return Math.abs(b - a);
}
};
_ContinuousScale.defaultTickCount = 5;
var ContinuousScale = _ContinuousScale;
function normalizeContinuousDomains(...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 };
}
}
// packages/ag-charts-community/src/scale/linearScale.ts
var LinearScale = class _LinearScale extends ContinuousScale {
constructor() {
super([0, 1], [0, 1]);
this.type = "number";
}
static is(value) {
return value instanceof _LinearScale;
}
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 { ticks: [], count: 0, firstTickIndex: 0 };
}
const [d0, d1] = domain;
if (interval) {
const step = Math.abs(interval);
if (!isDenseInterval((d1 - d0) / step, this.getPixelRange())) {
return range(d0, d1, step, visibleRange);
}
}
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];
}
};
// packages/ag-charts-community/src/scene/scene.ts
import { CleanupRegistry, EventEmitter as EventEmitter2, Logger as Logger12, createId as createId3, downloadUrl } from "ag-charts-core";
// packages/ag-charts-community/src/scene/canvas/hdpiCanvas.ts
import { createElement, getWindow as getWindow3 } from "ag-charts-core";
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 ?? getWindow3("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);
}
reset() {
this.context.reset();
this.context.verifyDepthZero?.();
}
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/image/imageLoader.ts
import { EventEmitter } from "ag-charts-core";
var ImageLoader = class extends EventEmitter {
constructor() {
super(...arguments);
this.cache = /* @__PURE__ */ new Map();
this.imageLoadingCount = 0;
}
loadImage(uri, affectedNode) {
const entry = this.cache.get(uri);
if (entry?.image) {
return entry.image;
} else if (entry != null && affectedNode) {
entry.nodes.add(affectedNode);
return;
}
if (!affectedNode) {
return;
}
const nextEntry = { image: void 0, nodes: /* @__PURE__ */ new Set([affectedNode]) };
const image = new Image();
this.imageLoadingCount++;
image.onload = () => {
nextEntry.image = image;
for (const node of nextEntry.nodes) {
node.markDirty();
}
nextEntry.nodes.clear();
this.imageLoadingCount--;
this.emit("image-loaded", { uri });
};
image.onerror = () => {
this.imageLoadingCount--;
nextEntry.nodes.clear();
this.emit("image-error", { uri });
};
image.src = uri;
this.cache.set(uri, nextEntry);
return nextEntry.image;
}
waitingToLoad() {
return this.imageLoadingCount > 0;
}
destroy() {
this.cache.forEach((entry) => {
entry.nodes.clear();
});
this.cache.clear();
}
};
// 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/scene.ts
var Scene = class extends EventEmitter2 {
constructor(canvasOptions) {
super();
this.debug = Debug.create(true, "scene" /* SCENE */);
this.id = createId3(this);
this.imageLoader = new ImageLoader();
this.root = null;
this.pendingSize = null;
this.isDirty = false;
this.cleanup = new CleanupRegistry();
this.updateDebugFlags();
this.canvas = new HdpiCanvas(canvasOptions);
this.layersManager = new LayersManager(this.canvas);
this.cleanup.register(
this.imageLoader.on("image-loaded", () => {
this.emit("scene-changed", {});
}),
this.imageLoader.on("image-error", ({ uri }) => {
Logger12.warnOnce(`Unable to load image ${uri}`);
})
);
}
waitingForUpdate() {
return this.imageLoader?.waitingToLoad() ?? false;
}
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?.setScene();
this.root = node;
if (node) {
node.visible = true;
node.setScene(this);
}
return this;
}
updateDebugFlags() {
Debug.inDevelopmentMode(() => Node._debugEnabled = true);
}
clearCanvas() {
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();
let resized = false;
if (pendingSize) {
resized = true;
this.layersManager.resize(...pendingSize);
this.pendingSize = null;
}
if (root && !root.visible) {
this.isDirty = false;
return;
}
let rootDirty;
if (root instanceof Group) {
rootDirty = root.dirty;
}
if (root != null && rootDirty === 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 (rootDirty !== false || resized) {
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) {
try {
ctx.save();
root.render(renderCtx);
ctx.restore();
} catch (e) {
this.canvas.reset();
throw e;
}
}
}
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;
this.clear();
}
destroy() {
this.strip();
this.canvas.destroy();
this.imageLoader.destroy();
this.cleanup.flush();
Object.assign(this, { canvas: void 0 });
}
};
Scene.className = "Scene";
// packages/ag-charts-community/src/scene/shape/arc.ts
import { isNumberEqual as isNumberEqual2 } from "ag-charts-core";
var Arc = class extends Path {
constructor() {
super(...arguments);
this.centerX = 0;
this.centerY = 0;
this.radius = 10;
this.startAngle = 0;
this.endAngle = Math.PI * 2;
this.counterClockwise = false;
this.type = 0 /* Open */;
}
get fullPie() {
return isNumberEqual2(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([
SceneChangeDetection()
], Arc.prototype, "centerX", 2);
__decorateClass([
SceneChangeDetection()
], Arc.prototype, "centerY", 2);
__decorateClass([
SceneChangeDetection()
], Arc.prototype, "radius", 2);
__decorateClass([
SceneChangeDetection()
], Arc.prototype, "startAngle", 2);
__decorateClass([
SceneChangeDetection()
], Arc.prototype, "endAngle", 2);
__decorateClass([
SceneChangeDetection()
], Arc.prototype, "counterClockwise", 2);
__decorateClass([
SceneChangeDetection()
], Arc.prototype, "type", 2);
// packages/ag-charts-community/src/scene/shape/line.ts
import { createSvgElement as createSvgElement11 } from "ag-charts-core";
var Line = class extends Shape {
constructor(opts = {}) {
super(opts);
this.x1 = 0;
this.y1 = 0;
this.x2 = 0;
this.y2 = 0;
this.fill = void 0;
this.strokeWidth = 1;
}
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 = createSvgElement11("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";
__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
import { isNumberEqual as isNumberEqual3 } from "ag-charts-core";
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 = isNumberEqual3(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 isEmpty = isNumberEqual3(innerRadius, outerRadius);
const hasSideIntersection = compare(
axisOuterRadius,
getTriangleHypotenuse(outerRadius, columnWidth / 2),
!isRadiusAxisReversed
);
if (isEmpty && 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 (!isNumberEqual3(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([
SceneChangeDetection()
], RadialColumnShape.prototype, "isBeveled", 2);
__decorateClass([
SceneChangeDetection()
], RadialColumnShape.prototype, "columnWidth", 2);
__decorateClass([
SceneChangeDetection()
], RadialColumnShape.prototype, "startAngle", 2);
__decorateClass([
SceneChangeDetection()
], RadialColumnShape.prototype, "endAngle", 2);
__decorateClass([
SceneChangeDetection()
], RadialColumnShape.prototype, "outerRadius", 2);
__decorateClass([
SceneChangeDetection()
], RadialColumnShape.prototype, "innerRadius", 2);
__decorateClass([
SceneChangeDetection()
], RadialColumnShape.prototype, "axisInnerRadius", 2);
__decorateClass([
SceneChangeDetection()
], RadialColumnShape.prototype, "axisOuterRadius", 2);
__decorateClass([
SceneChangeDetection()
], 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/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([
SceneChangeDetection()
], Sector.prototype, "centerX", 2);
__decorateClass([
SceneChangeDetection()
], Sector.prototype, "centerY", 2);
__decorateClass([
SceneChangeDetection()
], Sector.prototype, "innerRadius", 2);
__decorateClass([
SceneChangeDetection()
], Sector.prototype, "outerRadius", 2);
__decorateClass([
SceneChangeDetection()
], Sector.prototype, "startAngle", 2);
__decorateClass([
SceneChangeDetection()
], Sector.prototype, "endAngle", 2);
__decorateClass([
SceneObjectChangeDetection({ equals: (lhs, rhs) => lhs.equals(rhs) })
], Sector.prototype, "clipSector", 2);
__decorateClass([
SceneChangeDetection()
], Sector.prototype, "concentricEdgeInset", 2);
__decorateClass([
SceneChangeDetection()
], Sector.prototype, "radialEdgeInset", 2);
__decorateClass([
SceneChangeDetection()
], Sector.prototype, "startOuterCornerRadius", 2);
__decorateClass([
SceneChangeDetection()
], Sector.prototype, "endOuterCornerRadius", 2);
__decorateClass([
SceneChangeDetection()
], Sector.prototype, "startInnerCornerRadius", 2);
__decorateClass([
SceneChangeDetection()
], Sector.prototype, "endInnerCornerRadius", 2);
export {
Arc,
BBox,
Caption,
CategoryScale,
Group,
Line,
LinearScale,
Marker,
Path,
RadialColumnShape,
Rect,
Scene,
Sector,
Shape,
TranslatableGroup,
getRadialColumnWidth,
toRadians
};