UNPKG

scichart

Version:

Fast WebGL JavaScript Charting Library and Framework

709 lines (708 loc) 37 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.layoutLabelsHelper = exports.AxisRenderer = void 0; var app_1 = require("../../../constants/app"); var DeletableEntity_1 = require("../../../Core/DeletableEntity"); var Deleter_1 = require("../../../Core/Deleter"); var Rect_1 = require("../../../Core/Rect"); var Thickness_1 = require("../../../Core/Thickness"); var AxisAlignment_1 = require("../../../types/AxisAlignment"); var LabelAlignment_1 = require("../../../types/LabelAlignment"); var logger_1 = require("../../../utils/logger"); var parseColor_1 = require("../../../utils/parseColor"); var text_1 = require("../../../utils/text"); var WebGlRenderContext2D_1 = require("../../Drawing/WebGlRenderContext2D"); var createNativeRect_1 = require("../Helpers/createNativeRect"); var NativeObject_1 = require("../Helpers/NativeObject"); var SciChartSurfaceBase_1 = require("../SciChartSurfaceBase"); var DpiHelper_1 = require("../TextureManager/DpiHelper"); var TextureManager_1 = require("../TextureManager/TextureManager"); /** * Draws an axis using our WebGL Rendering engine. * Base class that provides shared layout/loop logic via a template method pattern. * Subclasses ({@link NativeAxisRenderer}, {@link TextureAxisRenderer}) override the * hooks {@link onBeginDrawLabels}, {@link drawSingleLabel}, and {@link onEndDrawLabels} * to provide rendering-path-specific behaviour. */ var AxisRenderer = /** @class */ (function (_super) { __extends(AxisRenderer, _super); /** * Creates an instance of a {@link AxisRenderer} * @param webAssemblyContext The {@link TSciChart | SciChart 2D WebAssembly Context} containing native methods and * access to our WebGL2 Engine and WebAssembly numerical methods */ function AxisRenderer(webAssemblyContext) { var _this = _super.call(this) || this; /** * The viewRect of the axis for ticks and labels. Does not include the axis Title. */ _this.viewRect = Rect_1.Rect.createZero(); _this.drawDebug = false; _this.desiredLabelsSize = 0; _this.desiredTicksSize = 0; _this.desiredHeightProperty = 0; _this.desiredWidthProperty = 0; _this.axisThicknessProperty = 0; _this.keepLabelsWithinAxisProperty = true; _this.hideOverlappingLabelsProperty = true; _this.webAssemblyContext = webAssemblyContext; _this.textureManager = new TextureManager_1.TextureManager(webAssemblyContext); if (!app_1.IS_TEST_ENV) { _this.measureTextCanvas = document.createElement("canvas"); _this.measureTextCanvas.width = 1; _this.measureTextCanvas.height = 1; } return _this; } /** @inheritDoc */ AxisRenderer.prototype.delete = function () { this.webAssemblyContext = undefined; this.measureTextCanvas = undefined; this.parentAxis = undefined; this.textureManager = (0, Deleter_1.deleteSafe)(this.textureManager); }; /** * Called when the {@link AxisRenderer} is attached to an {@link AxisBase2D | Axis} * @param axis The Axis we are attached to. */ AxisRenderer.prototype.attachedToAxis = function (axis) { this.parentAxis = axis; }; /** * Called internally - measures the axis label area as part of the layout phase */ AxisRenderer.prototype.measure = function (isHorizontalAxis, labelStyle, majorTickLabels, ticksSize, labelProvider, drawLabels, drawTicks, labelInfos) { if (SciChartSurfaceBase_1.DebugForDpi) { console.log("AxisRenderer.measure. fontSize: ".concat(labelStyle.fontSize)); } this.desiredLabelsSize = drawLabels ? this.calcDesiredLabelsSize(isHorizontalAxis, labelProvider, labelStyle, majorTickLabels, labelInfos) : 0; this.desiredTicksSize = drawTicks ? ticksSize : 0; var desiredLabelsSize = Math.max(this.desiredLabelsSize, this.axisThicknessProperty * DpiHelper_1.DpiHelper.PIXEL_RATIO); var desiredSize = desiredLabelsSize + this.desiredTicksSize; if (isHorizontalAxis) { this.desiredHeightProperty = desiredSize; } else { this.desiredWidthProperty = desiredSize; } }; /** * Called internally - calculates desired labels size */ AxisRenderer.prototype.calcDesiredLabelsSize = function (isHorizontalAxis, labelProvider, labelStyle, majorTickLabels, labelInfos) { if (app_1.IS_TEST_ENV) return 0; var desiredLabelsSize; var ctx = this.getMeasureContext(); if (isHorizontalAxis) { desiredLabelsSize = labelProvider.getMaxLabelHeightForHorizontalAxis(majorTickLabels, ctx, labelStyle, labelInfos); } else { desiredLabelsSize = labelProvider.getMaxLabelWidthForVerticalAxis(majorTickLabels, ctx, labelStyle, labelInfos); } // uncomment this line to get desiredLabelsSize to use in unit tests // console.log("calcDesiredLabelsSize", this.parentAxis.isXAxis ? "xAxis" : "yAxis", desiredLabelsSize); return desiredLabelsSize; }; Object.defineProperty(AxisRenderer.prototype, "desiredHeight", { /** * Called internally - Gets or sets desired height during the layout process */ get: function () { return this.desiredHeightProperty; }, set: function (height) { this.desiredHeightProperty = height; }, enumerable: false, configurable: true }); Object.defineProperty(AxisRenderer.prototype, "desiredWidth", { /** * Called internally - Gets or sets desired width during the layout process */ get: function () { return this.desiredWidthProperty; }, set: function (width) { this.desiredWidthProperty = width; }, enumerable: false, configurable: true }); Object.defineProperty(AxisRenderer.prototype, "keepLabelsWithinAxis", { /** * Gets or sets keepLabelsWithinAxis property. * When true (default), first and last labels will be shifted to stay within axis bounds. * If set to false, these labels will stay aligned to their ticks */ get: function () { return this.keepLabelsWithinAxisProperty; }, set: function (value) { this.keepLabelsWithinAxisProperty = value; this.invalidateParent(); }, enumerable: false, configurable: true }); Object.defineProperty(AxisRenderer.prototype, "hideOverlappingLabels", { /** * Gets or sets hideOverlappingLabels property. * Default (true) is to not show labels that would overlap. When using rotation you may want to set this false, * as the bounding box of rotated text may overlap even if the text itself does not. */ get: function () { return this.hideOverlappingLabelsProperty; }, set: function (value) { this.hideOverlappingLabelsProperty = value; this.invalidateParent(); }, enumerable: false, configurable: true }); Object.defineProperty(AxisRenderer.prototype, "axisThickness", { /** * Gets or sets axis label area thickness, by default the size is calculated to have enough space for labels. * However, this property allows to set minimal width/height for vertical/horizontal axes. * Useful to align seriesViewRects for different charts */ get: function () { return this.axisThicknessProperty; }, set: function (value) { this.axisThicknessProperty = value; this.invalidateParent(); }, enumerable: false, configurable: true }); /** * Called internally as a part of the layout process */ AxisRenderer.prototype.layout = function (rect) { this.viewRect = rect; // Not much we can do here as we don't have access to tick coordinates yet }; /** * Gets the clip rect */ AxisRenderer.prototype.getClipRect = function () { return this.parentAxis.parentSurface.clipRect; }; /** * Called before the label draw loop. Subclasses create rendering resources and return * the line height for label positioning. */ AxisRenderer.prototype.onBeginDrawLabels = function (renderContext, labelProvider, labelStyle) { return 0; }; /** * Render a single label at the computed position. * Subclasses override to provide native or texture rendering. */ AxisRenderer.prototype.drawSingleLabel = function (renderContext, labelProvider, labelText, labelInfo, xCoord, yCoord, tickCoord, labelWidth, labelHeight, index, labelStyle, clipRect, isHorizontal) { // no-op default }; /** * Called after the label draw loop. Subclasses clean up resources. */ AxisRenderer.prototype.onEndDrawLabels = function () { // no-op default }; /** * Measures label widths and heights. Subclasses may override for * rendering-path-specific measurement (e.g. native CalculateStringBounds). */ AxisRenderer.prototype.measureLabels = function (tickLabels, labelProvider, ctx, labelStyle, labelInfos) { // Fast path: if labelInfos are complete, extract dimensions directly // avoiding per-label conditional checks in getLabelHeight/getLabelWidth if (labelInfos && labelInfos.length === tickLabels.length) { var allComplete = true; var labelHeights = new Array(tickLabels.length); var labelWidths = new Array(tickLabels.length); for (var i = 0; i < labelInfos.length; i++) { var info = labelInfos[i]; if ((info === null || info === void 0 ? void 0 : info.textureHeight) && (info === null || info === void 0 ? void 0 : info.textureWidth)) { labelHeights[i] = info.textureHeight; labelWidths[i] = info.textureWidth; } else { allComplete = false; break; } } if (allComplete) return { labelHeights: labelHeights, labelWidths: labelWidths }; } return { labelHeights: tickLabels.map(function (label, i) { return labelProvider.getLabelHeight(ctx, label, labelStyle, labelInfos === null || labelInfos === void 0 ? void 0 : labelInfos[i]); }), labelWidths: tickLabels.map(function (label, i) { return labelProvider.getLabelWidth(ctx, label, labelStyle, labelInfos === null || labelInfos === void 0 ? void 0 : labelInfos[i]); }) }; }; /** * Called internally - draws labels */ AxisRenderer.prototype.drawLabels = function (renderContext, axisAlignment, isInnerAxis, tickLabels, tickCoords, axisOffset, labelStyle, isVerticalChart, isFlippedCoordinates, labelProvider, labelInfos) { if (SciChartSurfaceBase_1.DebugForDpi) { console.log("AxisRenderer.drawLabels. fontSize: ".concat(labelStyle.fontSize)); } var viewRect = this.viewRect; var isAxisFlipped = isVerticalChart ? (0, AxisAlignment_1.getIsHorizontal)(axisAlignment) : (0, AxisAlignment_1.getIsVertical)(axisAlignment); var width = Math.floor(viewRect.width); var height = Math.floor(viewRect.height); var tickSize = this.desiredTicksSize; var ctx = this.getMeasureContext(); labelProvider.ensureLabelInfoComplete(labelInfos, tickLabels, labelStyle); var padding = labelStyle.padding, alignment = labelStyle.alignment; if (isAxisFlipped) { tickCoords = tickCoords.slice().reverse(); tickLabels = tickLabels.slice().reverse(); labelInfos = labelInfos === null || labelInfos === void 0 ? void 0 : labelInfos.slice().reverse(); } // for debug var labelRects = []; var textColor = (0, parseColor_1.parseColorToUIntArgb)(labelStyle.color); var adjRotation = labelProvider.rotation; if (adjRotation > 90) adjRotation -= 180; else if (adjRotation < -90) adjRotation += 180; var rotationRad = -(adjRotation * Math.PI) / 180; // Why do we remove the offset for labels? tickCoords = tickCoords.map(function (t) { return t - axisOffset; }); // Set shared state for subclass hooks this.currentViewRect = viewRect; this.currentTextColor = textColor; this.currentRotationRad = rotationRad; // Template method: begin var lineHeight = this.onBeginDrawLabels(renderContext, labelProvider, labelStyle); this.currentLineHeight = lineHeight; var clipRect = this.getClipRect(); var isHorizontal = (0, AxisAlignment_1.getIsHorizontal)(axisAlignment); if (isHorizontal) { var _a = this.measureLabels(tickLabels, labelProvider, ctx, labelStyle, labelInfos), labelHeights = _a.labelHeights, labelWidths = _a.labelWidths; var _b = this.layoutLabels(width, tickCoords, labelWidths, isFlippedCoordinates, padding === null || padding === void 0 ? void 0 : padding.top, padding === null || padding === void 0 ? void 0 : padding.bottom), labelCoords = _b.labelCoords, labelIndexes = _b.labelIndexes; for (var index = 0; index < labelIndexes.length; index++) { var xCoord = labelCoords[index]; var labelText = tickLabels[labelIndexes[index]]; var labelHeight = labelHeights[labelIndexes[index]]; var labelWidth = labelWidths[labelIndexes[index]]; var yCoord = 0; // Always align to the axis for horizontal if ((axisAlignment === AxisAlignment_1.EAxisAlignment.Bottom && !isInnerAxis) || (axisAlignment === AxisAlignment_1.EAxisAlignment.Top && isInnerAxis)) { yCoord += tickSize; } else { yCoord += height - labelHeight - tickSize; } if (this.drawDebug) { labelRects.push(Rect_1.Rect.create(xCoord, yCoord, labelWidth, labelHeight)); } try { this.drawSingleLabel(renderContext, labelProvider, labelText, labelInfos === null || labelInfos === void 0 ? void 0 : labelInfos[labelIndexes[index]], xCoord, yCoord, tickCoords[labelIndexes[index]], labelWidth, labelHeight, index, labelStyle, clipRect, true); } catch (err) { logger_1.Logger.debug(err); // webgl context probably lost. Clear the label cache labelProvider.delete(); } } } else { var _c = this.measureLabels(tickLabels, labelProvider, ctx, labelStyle, labelInfos), labelHeights = _c.labelHeights, labelWidths = _c.labelWidths; var _d = this.layoutLabels(height, tickCoords, labelHeights, isFlippedCoordinates, padding === null || padding === void 0 ? void 0 : padding.left, padding === null || padding === void 0 ? void 0 : padding.right), labelCoords = _d.labelCoords, labelIndexes = _d.labelIndexes; for (var index = 0; index < labelIndexes.length; index++) { var xCoord = 0; var labelText = tickLabels[labelIndexes[index]]; var labelWidth = labelWidths[labelIndexes[index]]; var labelHeight = labelHeights[labelIndexes[index]]; xCoord = this.adjustForLabelAlignment(xCoord, labelWidth, alignment, axisAlignment, isInnerAxis, width, tickSize); var yCoord = labelCoords[index]; if (this.drawDebug) { labelRects.push(Rect_1.Rect.create(xCoord, yCoord, labelWidth, labelHeight)); } try { this.drawSingleLabel(renderContext, labelProvider, labelText, labelInfos === null || labelInfos === void 0 ? void 0 : labelInfos[labelIndexes[index]], xCoord, yCoord, tickCoords[labelIndexes[index]], labelWidth, labelHeight, index, labelStyle, clipRect, false); } catch (err) { logger_1.Logger.debug(err); // webgl context probably lost. Clear the label cache labelProvider.delete(); } } } // Template method: end this.onEndDrawLabels(); if (this.drawDebug) { this.drawLabelViewRects(renderContext, viewRect, labelRects); } }; /** * Called internally - adjusts labels for label alignment */ AxisRenderer.prototype.adjustForLabelAlignment = function (xCoord, labelWidth, labelAlignment, axisAlignment, isInnerAxis, axisWidth, tickSize) { var adj = axisWidth - tickSize - labelWidth; if ((axisAlignment === AxisAlignment_1.EAxisAlignment.Left && !isInnerAxis) || (axisAlignment === AxisAlignment_1.EAxisAlignment.Right && isInnerAxis)) { if (labelAlignment === LabelAlignment_1.ELabelAlignment.Right || labelAlignment === LabelAlignment_1.ELabelAlignment.Auto) { xCoord += adj; } else if (labelAlignment === LabelAlignment_1.ELabelAlignment.Center) { xCoord += adj / 2; } } else { if (labelAlignment === LabelAlignment_1.ELabelAlignment.Left || labelAlignment === LabelAlignment_1.ELabelAlignment.Auto) { xCoord += tickSize; } else if (labelAlignment === LabelAlignment_1.ELabelAlignment.Center) { xCoord += tickSize + adj / 2; } else { xCoord += axisWidth - labelWidth; } } return xCoord; }; /** * Called internally */ AxisRenderer.prototype.layoutLabels = function (size, tickCoords, labelSizes, isFlippedCoordinates, padBefore, padAfter) { var _a, _b; return (0, exports.layoutLabelsHelper)(this.keepLabelsWithinAxis, this.hideOverlappingLabels, size, tickCoords, labelSizes, isFlippedCoordinates, (_b = (_a = this.parentAxis) === null || _a === void 0 ? void 0 : _a.labelProvider) === null || _b === void 0 ? void 0 : _b.alwaysShowFirstLabel); }; /** * Called internally */ AxisRenderer.prototype.drawTicks = function (renderContext, axisAlignment, isInnerAxis, tickCoords, axisOffset, pen, tickStyle) { if (!tickCoords || tickCoords.length === 0) return; var viewRect = this.viewRect; var tickSize = tickStyle.tickSize; var vertices = (0, NativeObject_1.getVectorColorVertex)(this.webAssemblyContext); var isHorizontal = (0, AxisAlignment_1.getIsHorizontal)(axisAlignment); var vertex = (0, NativeObject_1.getVertex)(this.webAssemblyContext, 0, 0); if (isHorizontal === undefined) { return; } tickCoords.forEach(function (tc) { var x1, x2, y1, y2; if (isInnerAxis) { if (isHorizontal) { x1 = tc; x2 = tc; y1 = axisAlignment === AxisAlignment_1.EAxisAlignment.Top ? 0 : viewRect.height; y2 = axisAlignment === AxisAlignment_1.EAxisAlignment.Top ? tickSize : viewRect.height - tickSize; } else { x1 = axisAlignment === AxisAlignment_1.EAxisAlignment.Left ? 0 : viewRect.width; x2 = axisAlignment === AxisAlignment_1.EAxisAlignment.Left ? tickSize : viewRect.width - tickSize; y1 = tc; y2 = tc; } } else { if (isHorizontal) { x1 = tc; x2 = tc; y1 = axisAlignment === AxisAlignment_1.EAxisAlignment.Bottom ? 0 : viewRect.height; y2 = axisAlignment === AxisAlignment_1.EAxisAlignment.Bottom ? tickSize : viewRect.height - tickSize; } else { x1 = axisAlignment === AxisAlignment_1.EAxisAlignment.Right ? 0 : viewRect.width; x2 = axisAlignment === AxisAlignment_1.EAxisAlignment.Right ? tickSize : viewRect.width - tickSize; y1 = tc; y2 = tc; } } vertex.SetPosition(x1, y1); vertices.push_back(vertex); vertex.SetPosition(x2, y2); vertices.push_back(vertex); }); var leftOffset = viewRect.left - (isHorizontal ? axisOffset : 0); var topOffset = viewRect.top - (isHorizontal ? 0 : axisOffset); renderContext.drawLinesNative(vertices, pen, WebGlRenderContext2D_1.ELineDrawMode.DiscontinuousLine, this.getClipRect(), leftOffset, topOffset); }; /** * Used to get the offset from either edge of the seriesViewRect up until the axis labels */ AxisRenderer.prototype.getAxisReservedSpace = function (isDomAnnotation) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y; var reservedSpace = 0; // ticks if ((_a = this.parentAxis) === null || _a === void 0 ? void 0 : _a.drawMajorTickLines) { reservedSpace = Math.max(reservedSpace, (_c = (_b = this.parentAxis.majorTickLineStyle) === null || _b === void 0 ? void 0 : _b.tickSize) !== null && _c !== void 0 ? _c : 0); } if ((_d = this.parentAxis) === null || _d === void 0 ? void 0 : _d.drawMinorTickLines) { reservedSpace = Math.max(reservedSpace, (_f = (_e = this.parentAxis.minorTickLineStyle) === null || _e === void 0 ? void 0 : _e.tickSize) !== null && _f !== void 0 ? _f : 0); } // axis label padding + axis border var alignment = (_g = this.parentAxis) === null || _g === void 0 ? void 0 : _g.axisAlignment; switch (alignment) { case AxisAlignment_1.EAxisAlignment.Left: reservedSpace += (_j = (_h = this.parentAxis.labelStyle) === null || _h === void 0 ? void 0 : _h.padding.right) !== null && _j !== void 0 ? _j : 0; reservedSpace += (_l = (_k = this.parentAxis.axisBorder.borderRight) !== null && _k !== void 0 ? _k : this.parentAxis.axisBorder.border) !== null && _l !== void 0 ? _l : 0; break; case AxisAlignment_1.EAxisAlignment.Right: reservedSpace += (_o = (_m = this.parentAxis.labelStyle) === null || _m === void 0 ? void 0 : _m.padding.left) !== null && _o !== void 0 ? _o : 0; reservedSpace += (_q = (_p = this.parentAxis.axisBorder.borderLeft) !== null && _p !== void 0 ? _p : this.parentAxis.axisBorder.border) !== null && _q !== void 0 ? _q : 0; break; case AxisAlignment_1.EAxisAlignment.Top: reservedSpace += (_s = (_r = this.parentAxis.labelStyle) === null || _r === void 0 ? void 0 : _r.padding.bottom) !== null && _s !== void 0 ? _s : 0; reservedSpace += (_u = (_t = this.parentAxis.axisBorder.borderBottom) !== null && _t !== void 0 ? _t : this.parentAxis.axisBorder.border) !== null && _u !== void 0 ? _u : 0; break; case AxisAlignment_1.EAxisAlignment.Bottom: reservedSpace += (_w = (_v = this.parentAxis.labelStyle) === null || _v === void 0 ? void 0 : _v.padding.top) !== null && _w !== void 0 ? _w : 0; reservedSpace += (_y = (_x = this.parentAxis.axisBorder.borderTop) !== null && _x !== void 0 ? _x : this.parentAxis.axisBorder.border) !== null && _y !== void 0 ? _y : 0; break; } // natural text padding adjustments reservedSpace -= 1.5; if (alignment === AxisAlignment_1.EAxisAlignment.Top || alignment === AxisAlignment_1.EAxisAlignment.Bottom) { reservedSpace -= 4 / DpiHelper_1.DpiHelper.PIXEL_RATIO; } var innerAxisMultiplier = this.parentAxis.isInnerAxis ? -1 : 1; return reservedSpace * innerAxisMultiplier; }; /** * Measures modifier-axis-label content for the active renderer path. * Subclasses override to provide native or texture-specific sizing. */ AxisRenderer.prototype.measureModifierAxisLabel = function (renderContext, displayValue, textStyle, fill, effectivePadding, cornerRadius) { var texture = this.textureManager.createSimpleTextTexture(displayValue, __assign(__assign({}, textStyle), { padding: effectivePadding }), fill, undefined, undefined, undefined, cornerRadius); if (texture.bitmapTexture) { texture.bitmapTexture.delete(); } return { textureWidth: texture.textureWidth, textureHeight: texture.textureHeight }; }; /** * Draws modifier-axis-label content for the active renderer path. * Subclasses override to provide native or texture-specific drawing. */ AxisRenderer.prototype.drawModifierAxisLabelSpecific = function (renderContext, displayValue, textStyle, fill, effectivePadding, cornerRadius, xPosition, yPosition, textureWidth, textureHeight, _clipRect) { var texture = this.textureManager.createSimpleTextTexture(displayValue, __assign(__assign({}, textStyle), { padding: effectivePadding }), fill, undefined, undefined, undefined, cornerRadius); if (texture.bitmapTexture) { renderContext.drawTexture(texture.bitmapTexture, Math.round(xPosition), Math.round(yPosition), textureWidth, textureHeight); texture.bitmapTexture.delete(); } }; /** * Called internally - draws axis labels when needed, for example for line annotations */ AxisRenderer.prototype.drawModifiersAxisLabel = function (renderContext, displayValue, coord, axisAlignment, textStyle, fill, padding, cornerRadius) { if (!displayValue) return undefined; var parentSurface = this.parentAxis.parentSurface; var clipRect = Rect_1.Rect.intersect(parentSurface.viewRect, parentSurface.clipRect); var effectivePadding = new Thickness_1.Thickness(padding.top + 2 * DpiHelper_1.DpiHelper.PIXEL_RATIO, padding.right, padding.bottom + 2 * DpiHelper_1.DpiHelper.PIXEL_RATIO, padding.left); renderContext.resetAndClip(clipRect); var _a = this.measureModifierAxisLabel(renderContext, displayValue, textStyle, fill, effectivePadding, cornerRadius), textureHeight = _a.textureHeight, textureWidth = _a.textureWidth; var getPosition = function (viewRectOffset, coord, textureSize$) { return viewRectOffset + coord - (textureSize$ / 2); }; var offset = this.parentAxis.offset; var xPosition = 0, yPosition = 0; var scaledReserved = this.getAxisReservedSpace() * DpiHelper_1.DpiHelper.PIXEL_RATIO; switch (axisAlignment) { case AxisAlignment_1.EAxisAlignment.Top: { xPosition = getPosition(this.viewRect.x - offset, coord, textureWidth); if (this.parentAxis.isInnerAxis) { yPosition = this.viewRect.y; } else { yPosition = this.viewRect.y + this.viewRect.height - textureHeight; } yPosition -= scaledReserved; break; } case AxisAlignment_1.EAxisAlignment.Bottom: { xPosition = getPosition(this.viewRect.x - offset, coord, textureWidth); if (this.parentAxis.isInnerAxis) { yPosition = this.viewRect.y + this.viewRect.height - textureHeight; } else { yPosition = this.viewRect.y; } yPosition += scaledReserved; break; } case AxisAlignment_1.EAxisAlignment.Left: { yPosition = getPosition(this.viewRect.y - offset, coord, textureHeight); if (this.parentAxis.isInnerAxis) { xPosition = this.viewRect.x; } else { xPosition = this.viewRect.x + this.viewRect.width - textureWidth; } xPosition -= scaledReserved; break; } case AxisAlignment_1.EAxisAlignment.Right: { yPosition = getPosition(this.viewRect.y - offset, coord, textureHeight); if (this.parentAxis.isInnerAxis) { xPosition = this.viewRect.x + this.viewRect.width - textureWidth; } else { xPosition = this.viewRect.x; } xPosition += scaledReserved; break; } } this.drawModifierAxisLabelSpecific(renderContext, displayValue, textStyle, fill, effectivePadding, cornerRadius, xPosition, yPosition, textureWidth, textureHeight, clipRect); return new Rect_1.Rect(xPosition, yPosition, textureWidth, textureHeight); }; /** * Called internally - used for {@link AxisMarkerAnnotation} */ AxisRenderer.prototype.createAxisMarker = function (axisAlignment, text, textStyle, backgroundColor, opacity) { var fontStyle = textStyle.fontStyle, fontWeight = textStyle.fontWeight, fontSize = textStyle.fontSize, fontFamily = textStyle.fontFamily, color = textStyle.color; return this.textureManager.createAxisMarkerTexture(axisAlignment, text, fontStyle, fontWeight, fontSize, fontFamily, color, 2 * DpiHelper_1.DpiHelper.PIXEL_RATIO, backgroundColor, opacity); }; /** * Called internally - used for custom {@link AxisMarkerAnnotation} */ AxisRenderer.prototype.createAxisMarkerFromImage = function (image, imageWidth, imageHeight) { return this.textureManager.createTextureFromImage(image, imageWidth, imageHeight); }; /** * Called internally */ AxisRenderer.prototype.createAnnotationLabelTexture = function (text, textStyle, backgroundColor, displayVertically, displayMirrored, opacity, cornerRadius) { return this.textureManager.createSimpleTextTexture(text, textStyle, backgroundColor, displayVertically, displayMirrored, opacity, cornerRadius); }; AxisRenderer.prototype.invalidateParent = function () { if (this.parentAxis && this.parentAxis.invalidateParentCallback) { this.parentAxis.invalidateParentCallback(); } }; AxisRenderer.prototype.drawLabelViewRects = function (renderContext, axisRect, rects) { var vecRects = (0, NativeObject_1.getVectorRectVertex)(this.webAssemblyContext); var brush = new this.webAssemblyContext.SCRTSolidBrush((0, parseColor_1.parseColorToUIntArgb)("rgba(30,30,255,0.7)"), false); for (var _i = 0, rects_1 = rects; _i < rects_1.length; _i++) { var rect = rects_1[_i]; var nativeRect = (0, createNativeRect_1.createNativeRect)(this.webAssemblyContext, rect.left, rect.top, rect.right, rect.bottom); vecRects.push_back(nativeRect); } renderContext.drawRects(vecRects, brush, this.getClipRect(), axisRect.left, axisRect.top); brush.delete(); }; AxisRenderer.prototype.calculateLineHightForNativeFont = function (nativeFont, textBounds) { if (nativeFont && textBounds) { nativeFont.CalculateStringBounds("Ag", textBounds, 0); var lineBounds = textBounds.GetLineBounds(0); // here we need height from Ascent to Baseline // https://stackoverflow.com/questions/27631736/meaning-of-top-ascent-baseline-descent-bottom-and-leading-in-androids-font var height = (0, text_1.getFirstLineHeightToBaseline)(lineBounds); lineBounds.delete(); return height; } return 0; }; AxisRenderer.prototype.getMeasureContext = function () { if (!app_1.IS_TEST_ENV) { return this.measureTextCanvas.getContext("2d", { willReadFrequently: true }); } return undefined; }; return AxisRenderer; }(DeletableEntity_1.DeletableEntity)); exports.AxisRenderer = AxisRenderer; /** @ignore */ var layoutLabelsHelper = function (keepLabelsWithinAxis, hideOverlappingLabels, size, tickCoords, labelSizes, isFlippedCoordinates, prioritiseFirstWideLabel) { if (prioritiseFirstWideLabel === void 0) { prioritiseFirstWideLabel = false; } var labelSpacing = 0; var labelCoords = []; var labelIndexes = []; var length = labelSizes.length; var getTickCoord = function (i) { return (isFlippedCoordinates ? tickCoords[length - 1 - i] : tickCoords[i]); }; var getlabelSize = function (i) { return (isFlippedCoordinates ? labelSizes[length - 1 - i] : labelSizes[i]); }; var pushLabelIndex = function (i) { return isFlippedCoordinates ? labelIndexes.push(length - 1 - i) : labelIndexes.push(i); }; var lastLabelEnd = 0; for (var tickIndex = 0; tickIndex < length; tickIndex++) { var isFirstTick = tickIndex === 0; var isLastTick = tickIndex === length - 1; var labelSize = getlabelSize(tickIndex); var centerDelta = Math.ceil(labelSize / 2); var coord = getTickCoord(tickIndex); var labelEnd = 0; if (keepLabelsWithinAxis && isFirstTick) { if (coord > centerDelta) { labelEnd = coord + centerDelta; coord = coord - centerDelta; } else { coord = 0; labelEnd = coord + labelSize; } if (!prioritiseFirstWideLabel) { var nextCoord = getTickCoord(1); var nextCenterDelta = getlabelSize(1) / 2; if (hideOverlappingLabels) { // Skip first if it would overlap with next if (labelEnd >= nextCoord - nextCenterDelta - labelSpacing) { continue; } } } } else if (keepLabelsWithinAxis && isLastTick) { coord = coord + centerDelta < size ? coord - centerDelta : size - labelSize; } else { labelEnd = coord + centerDelta; coord -= centerDelta; } if (hideOverlappingLabels) { // If this label will overlap the previous, skip it. if (tickIndex > 0 && coord < lastLabelEnd + labelSpacing) { continue; } } lastLabelEnd = labelEnd; labelCoords.push(coord); pushLabelIndex(tickIndex); } return { labelCoords: labelCoords, labelIndexes: labelIndexes }; }; exports.layoutLabelsHelper = layoutLabelsHelper;