596 lines
20 KiB
JavaScript
596 lines
20 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
const {
|
|
apply,
|
|
getNodeTransformationMatrix,
|
|
getWritingModeMatrix,
|
|
identity,
|
|
isIdentity,
|
|
multiply,
|
|
scale,
|
|
translate,
|
|
} = require("resource://devtools/shared/layout/dom-matrix-2d.js");
|
|
const {
|
|
getCurrentZoom,
|
|
getViewportDimensions,
|
|
} = require("resource://devtools/shared/layout/utils.js");
|
|
const {
|
|
getComputedStyle,
|
|
} = require("resource://devtools/server/actors/highlighters/utils/markup.js");
|
|
|
|
// A set of utility functions for highlighters that render their content to a <canvas>
|
|
// element.
|
|
|
|
// We create a <canvas> element that has always 4096x4096 physical pixels, to displays
|
|
// our grid's overlay.
|
|
// Then, we move the element around when needed, to give the perception that it always
|
|
// covers the screen (See bug 1345434).
|
|
//
|
|
// This canvas size value is the safest we can use because most GPUs can handle it.
|
|
// It's also far from the maximum canvas memory allocation limit (4096x4096x4 is
|
|
// 67.108.864 bytes, where the limit is 500.000.000 bytes, see
|
|
// gfx_max_alloc_size in modules/libpref/init/StaticPrefList.yaml.
|
|
//
|
|
// Note:
|
|
// Once bug 1232491 lands, we could try to refactor this code to use the values from
|
|
// the displayport API instead.
|
|
//
|
|
// Using a fixed value should also solve bug 1348293.
|
|
const CANVAS_SIZE = 4096;
|
|
|
|
// The default color used for the canvas' font, fill and stroke colors.
|
|
const DEFAULT_COLOR = "#9400FF";
|
|
|
|
/**
|
|
* Draws a rect to the context given and applies a transformation matrix if passed.
|
|
* The coordinates are the start and end points of the rectangle's diagonal.
|
|
*
|
|
* @param {CanvasRenderingContext2D} ctx
|
|
* The 2D canvas context.
|
|
* @param {Number} x1
|
|
* The x-axis coordinate of the rectangle's diagonal start point.
|
|
* @param {Number} y1
|
|
* The y-axis coordinate of the rectangle's diagonal start point.
|
|
* @param {Number} x2
|
|
* The x-axis coordinate of the rectangle's diagonal end point.
|
|
* @param {Number} y2
|
|
* The y-axis coordinate of the rectangle's diagonal end point.
|
|
* @param {Array} [matrix=identity()]
|
|
* The transformation matrix to apply.
|
|
*/
|
|
function clearRect(ctx, x1, y1, x2, y2, matrix = identity()) {
|
|
const p = getPointsFromDiagonal(x1, y1, x2, y2, matrix);
|
|
|
|
// We are creating a clipping path and want it removed after we clear it's
|
|
// contents so we need to save the context.
|
|
ctx.save();
|
|
|
|
// Create a path to be cleared.
|
|
ctx.beginPath();
|
|
ctx.moveTo(Math.round(p[0].x), Math.round(p[0].y));
|
|
ctx.lineTo(Math.round(p[1].x), Math.round(p[1].y));
|
|
ctx.lineTo(Math.round(p[2].x), Math.round(p[2].y));
|
|
ctx.lineTo(Math.round(p[3].x), Math.round(p[3].y));
|
|
ctx.closePath();
|
|
|
|
// Restrict future drawing to the inside of the path.
|
|
ctx.clip();
|
|
|
|
// Clear any transforms applied to the canvas so that clearRect() really does
|
|
// clear everything.
|
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
|
|
// Clear the contents of our clipped path by attempting to clear the canvas.
|
|
ctx.clearRect(0, 0, CANVAS_SIZE, CANVAS_SIZE);
|
|
|
|
// Restore the context to the state it was before changing transforms and
|
|
// adding clipping paths.
|
|
ctx.restore();
|
|
}
|
|
|
|
/**
|
|
* Draws an arrow-bubble rectangle in the provided canvas context.
|
|
*
|
|
* @param {CanvasRenderingContext2D} ctx
|
|
* The 2D canvas context.
|
|
* @param {Number} x
|
|
* The x-axis origin of the rectangle.
|
|
* @param {Number} y
|
|
* The y-axis origin of the rectangle.
|
|
* @param {Number} width
|
|
* The width of the rectangle.
|
|
* @param {Number} height
|
|
* The height of the rectangle.
|
|
* @param {Number} radius
|
|
* The radius of the rounding.
|
|
* @param {Number} margin
|
|
* The distance of the origin point from the pointer.
|
|
* @param {Number} arrowSize
|
|
* The size of the arrow.
|
|
* @param {String} alignment
|
|
* The alignment of the rectangle in relation to its position to the grid.
|
|
*/
|
|
function drawBubbleRect(
|
|
ctx,
|
|
x,
|
|
y,
|
|
width,
|
|
height,
|
|
radius,
|
|
margin,
|
|
arrowSize,
|
|
alignment
|
|
) {
|
|
let angle = 0;
|
|
|
|
if (alignment === "bottom") {
|
|
angle = 180;
|
|
} else if (alignment === "right") {
|
|
angle = 90;
|
|
[width, height] = [height, width];
|
|
} else if (alignment === "left") {
|
|
[width, height] = [height, width];
|
|
angle = 270;
|
|
}
|
|
|
|
const originX = x;
|
|
const originY = y;
|
|
|
|
ctx.save();
|
|
ctx.translate(originX, originY);
|
|
ctx.rotate(angle * (Math.PI / 180));
|
|
ctx.translate(-originX, -originY);
|
|
ctx.translate(-width / 2, -height - arrowSize - margin);
|
|
|
|
// The contour of the bubble is drawn with a path. The canvas context will have taken
|
|
// care of transforming the coordinates before calling the function, so we just always
|
|
// draw with the arrow pointing down. The top edge has rounded corners too.
|
|
ctx.beginPath();
|
|
// Start at the top/left corner (below the rounded corner).
|
|
ctx.moveTo(x, y + radius);
|
|
// Go down.
|
|
ctx.lineTo(x, y + height);
|
|
// Go down and the right, to draw the first half of the arrow tip.
|
|
ctx.lineTo(x + width / 2, y + height + arrowSize);
|
|
// Go back up and to the right, to draw the second half of the arrow tip.
|
|
ctx.lineTo(x + width, y + height);
|
|
// Go up to just below the top/right rounded corner.
|
|
ctx.lineTo(x + width, y + radius);
|
|
// Draw the top/right rounded corner.
|
|
ctx.arcTo(x + width, y, x + width - radius, y, radius);
|
|
// Go to the left.
|
|
ctx.lineTo(x + radius, y);
|
|
// Draw the top/left rounded corner.
|
|
ctx.arcTo(x, y, x, y + radius, radius);
|
|
|
|
ctx.stroke();
|
|
ctx.fill();
|
|
|
|
ctx.restore();
|
|
}
|
|
|
|
/**
|
|
* Draws a line to the context given and applies a transformation matrix if passed.
|
|
*
|
|
* @param {CanvasRenderingContext2D} ctx
|
|
* The 2D canvas context.
|
|
* @param {Number} x1
|
|
* The x-axis of the coordinate for the begin of the line.
|
|
* @param {Number} y1
|
|
* The y-axis of the coordinate for the begin of the line.
|
|
* @param {Number} x2
|
|
* The x-axis of the coordinate for the end of the line.
|
|
* @param {Number} y2
|
|
* The y-axis of the coordinate for the end of the line.
|
|
* @param {Object} [options]
|
|
* The options object.
|
|
* @param {Array} [options.matrix=identity()]
|
|
* The transformation matrix to apply.
|
|
* @param {Array} [options.extendToBoundaries]
|
|
* If set, the line will be extended to reach the boundaries specified.
|
|
*/
|
|
function drawLine(ctx, x1, y1, x2, y2, options) {
|
|
const matrix = options.matrix || identity();
|
|
|
|
const p1 = apply(matrix, [x1, y1]);
|
|
const p2 = apply(matrix, [x2, y2]);
|
|
|
|
x1 = p1[0];
|
|
y1 = p1[1];
|
|
x2 = p2[0];
|
|
y2 = p2[1];
|
|
|
|
if (options.extendToBoundaries) {
|
|
if (p1[1] === p2[1]) {
|
|
x1 = options.extendToBoundaries[0];
|
|
x2 = options.extendToBoundaries[2];
|
|
} else {
|
|
y1 = options.extendToBoundaries[1];
|
|
x1 = ((p2[0] - p1[0]) * (y1 - p1[1])) / (p2[1] - p1[1]) + p1[0];
|
|
y2 = options.extendToBoundaries[3];
|
|
x2 = ((p2[0] - p1[0]) * (y2 - p1[1])) / (p2[1] - p1[1]) + p1[0];
|
|
}
|
|
}
|
|
|
|
ctx.beginPath();
|
|
ctx.moveTo(Math.round(x1), Math.round(y1));
|
|
ctx.lineTo(Math.round(x2), Math.round(y2));
|
|
}
|
|
|
|
/**
|
|
* Draws a rect to the context given and applies a transformation matrix if passed.
|
|
* The coordinates are the start and end points of the rectangle's diagonal.
|
|
*
|
|
* @param {CanvasRenderingContext2D} ctx
|
|
* The 2D canvas context.
|
|
* @param {Number} x1
|
|
* The x-axis coordinate of the rectangle's diagonal start point.
|
|
* @param {Number} y1
|
|
* The y-axis coordinate of the rectangle's diagonal start point.
|
|
* @param {Number} x2
|
|
* The x-axis coordinate of the rectangle's diagonal end point.
|
|
* @param {Number} y2
|
|
* The y-axis coordinate of the rectangle's diagonal end point.
|
|
* @param {Array} [matrix=identity()]
|
|
* The transformation matrix to apply.
|
|
*/
|
|
function drawRect(ctx, x1, y1, x2, y2, matrix = identity()) {
|
|
const p = getPointsFromDiagonal(x1, y1, x2, y2, matrix);
|
|
|
|
ctx.beginPath();
|
|
ctx.moveTo(Math.round(p[0].x), Math.round(p[0].y));
|
|
ctx.lineTo(Math.round(p[1].x), Math.round(p[1].y));
|
|
ctx.lineTo(Math.round(p[2].x), Math.round(p[2].y));
|
|
ctx.lineTo(Math.round(p[3].x), Math.round(p[3].y));
|
|
ctx.closePath();
|
|
}
|
|
|
|
/**
|
|
* Draws a rounded rectangle in the provided canvas context.
|
|
*
|
|
* @param {CanvasRenderingContext2D} ctx
|
|
* The 2D canvas context.
|
|
* @param {Number} x
|
|
* The x-axis origin of the rectangle.
|
|
* @param {Number} y
|
|
* The y-axis origin of the rectangle.
|
|
* @param {Number} width
|
|
* The width of the rectangle.
|
|
* @param {Number} height
|
|
* The height of the rectangle.
|
|
* @param {Number} radius
|
|
* The radius of the rounding.
|
|
*/
|
|
function drawRoundedRect(ctx, x, y, width, height, radius) {
|
|
ctx.beginPath();
|
|
ctx.moveTo(x, y + radius);
|
|
ctx.lineTo(x, y + height - radius);
|
|
ctx.arcTo(x, y + height, x + radius, y + height, radius);
|
|
ctx.lineTo(x + width - radius, y + height);
|
|
ctx.arcTo(x + width, y + height, x + width, y + height - radius, radius);
|
|
ctx.lineTo(x + width, y + radius);
|
|
ctx.arcTo(x + width, y, x + width - radius, y, radius);
|
|
ctx.lineTo(x + radius, y);
|
|
ctx.arcTo(x, y, x, y + radius, radius);
|
|
ctx.stroke();
|
|
ctx.fill();
|
|
}
|
|
|
|
/**
|
|
* Given an array of four points and returns a DOMRect-like object representing the
|
|
* boundaries defined by the four points.
|
|
*
|
|
* @param {Array} points
|
|
* An array with 4 pointer objects {x, y} representing the box quads.
|
|
* @return {Object} DOMRect-like object of the 4 points.
|
|
*/
|
|
function getBoundsFromPoints(points) {
|
|
const bounds = {};
|
|
|
|
bounds.left = Math.min(points[0].x, points[1].x, points[2].x, points[3].x);
|
|
bounds.right = Math.max(points[0].x, points[1].x, points[2].x, points[3].x);
|
|
bounds.top = Math.min(points[0].y, points[1].y, points[2].y, points[3].y);
|
|
bounds.bottom = Math.max(points[0].y, points[1].y, points[2].y, points[3].y);
|
|
|
|
bounds.x = bounds.left;
|
|
bounds.y = bounds.top;
|
|
bounds.width = bounds.right - bounds.left;
|
|
bounds.height = bounds.bottom - bounds.top;
|
|
|
|
return bounds;
|
|
}
|
|
|
|
/**
|
|
* Returns the current matrices for both canvas drawing and SVG taking into account the
|
|
* following transformations, in this order:
|
|
* 1. The scale given by the display pixel ratio.
|
|
* 2. The translation to the top left corner of the element.
|
|
* 3. The scale given by the current zoom.
|
|
* 4. The translation given by the top and left padding of the element.
|
|
* 5. Any CSS transformation applied directly to the element (only 2D
|
|
* transformation; the 3D transformation are flattened, see `dom-matrix-2d` module
|
|
* for further details.)
|
|
* 6. Rotate, translate, and reflect as needed to match the writing mode and text
|
|
* direction of the element.
|
|
*
|
|
* The transformations of the element's ancestors are not currently computed (see
|
|
* bug 1355675).
|
|
*
|
|
* @param {Element} element
|
|
* The current element.
|
|
* @param {Window} window
|
|
* The window object.
|
|
* @param {Object} [options.ignoreWritingModeAndTextDirection=false]
|
|
* Avoid transforming the current matrix to match the text direction
|
|
* and writing mode.
|
|
* @return {Object} An object with the following properties:
|
|
* - {Array} currentMatrix
|
|
* The current matrix.
|
|
* - {Boolean} hasNodeTransformations
|
|
* true if the node has transformed and false otherwise.
|
|
*/
|
|
function getCurrentMatrix(
|
|
element,
|
|
window,
|
|
{ ignoreWritingModeAndTextDirection } = {}
|
|
) {
|
|
const computedStyle = getComputedStyle(element);
|
|
|
|
const paddingTop = parseFloat(computedStyle.paddingTop);
|
|
const paddingRight = parseFloat(computedStyle.paddingRight);
|
|
const paddingBottom = parseFloat(computedStyle.paddingBottom);
|
|
const paddingLeft = parseFloat(computedStyle.paddingLeft);
|
|
const borderTop = parseFloat(computedStyle.borderTopWidth);
|
|
const borderRight = parseFloat(computedStyle.borderRightWidth);
|
|
const borderBottom = parseFloat(computedStyle.borderBottomWidth);
|
|
const borderLeft = parseFloat(computedStyle.borderLeftWidth);
|
|
|
|
const nodeMatrix = getNodeTransformationMatrix(
|
|
element,
|
|
window.document.documentElement
|
|
);
|
|
|
|
let currentMatrix = identity();
|
|
let hasNodeTransformations = false;
|
|
|
|
// Scale based on the device pixel ratio.
|
|
currentMatrix = multiply(currentMatrix, scale(window.devicePixelRatio));
|
|
|
|
// Apply the current node's transformation matrix, relative to the inspected window's
|
|
// root element, but only if it's not a identity matrix.
|
|
if (isIdentity(nodeMatrix)) {
|
|
hasNodeTransformations = false;
|
|
} else {
|
|
currentMatrix = multiply(currentMatrix, nodeMatrix);
|
|
hasNodeTransformations = true;
|
|
}
|
|
|
|
// Translate the origin based on the node's padding and border values.
|
|
currentMatrix = multiply(
|
|
currentMatrix,
|
|
translate(paddingLeft + borderLeft, paddingTop + borderTop)
|
|
);
|
|
|
|
// Adjust as needed to match the writing mode and text direction of the element.
|
|
const size = {
|
|
width:
|
|
element.offsetWidth -
|
|
borderLeft -
|
|
borderRight -
|
|
paddingLeft -
|
|
paddingRight,
|
|
height:
|
|
element.offsetHeight -
|
|
borderTop -
|
|
borderBottom -
|
|
paddingTop -
|
|
paddingBottom,
|
|
};
|
|
|
|
if (!ignoreWritingModeAndTextDirection) {
|
|
const writingModeMatrix = getWritingModeMatrix(size, computedStyle);
|
|
if (!isIdentity(writingModeMatrix)) {
|
|
currentMatrix = multiply(currentMatrix, writingModeMatrix);
|
|
}
|
|
}
|
|
|
|
return { currentMatrix, hasNodeTransformations };
|
|
}
|
|
|
|
/**
|
|
* Given an array of four points, returns a string represent a path description.
|
|
*
|
|
* @param {Array} points
|
|
* An array with 4 pointer objects {x, y} representing the box quads.
|
|
* @return {String} a Path Description that can be used in svg's <path> element.
|
|
*/
|
|
function getPathDescriptionFromPoints(points) {
|
|
return (
|
|
"M" +
|
|
points[0].x +
|
|
"," +
|
|
points[0].y +
|
|
" " +
|
|
"L" +
|
|
points[1].x +
|
|
"," +
|
|
points[1].y +
|
|
" " +
|
|
"L" +
|
|
points[2].x +
|
|
"," +
|
|
points[2].y +
|
|
" " +
|
|
"L" +
|
|
points[3].x +
|
|
"," +
|
|
points[3].y
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Given the rectangle's diagonal start and end coordinates, returns an array containing
|
|
* the four coordinates of a rectangle. If a matrix is provided, applies the matrix
|
|
* function to each of the coordinates' value.
|
|
*
|
|
* @param {Number} x1
|
|
* The x-axis coordinate of the rectangle's diagonal start point.
|
|
* @param {Number} y1
|
|
* The y-axis coordinate of the rectangle's diagonal start point.
|
|
* @param {Number} x2
|
|
* The x-axis coordinate of the rectangle's diagonal end point.
|
|
* @param {Number} y2
|
|
* The y-axis coordinate of the rectangle's diagonal end point.
|
|
* @param {Array} [matrix=identity()]
|
|
* A transformation matrix to apply.
|
|
* @return {Array} the four coordinate points of the given rectangle transformed by the
|
|
* matrix given.
|
|
*/
|
|
function getPointsFromDiagonal(x1, y1, x2, y2, matrix = identity()) {
|
|
return [
|
|
[x1, y1],
|
|
[x2, y1],
|
|
[x2, y2],
|
|
[x1, y2],
|
|
].map(point => {
|
|
const transformedPoint = apply(matrix, point);
|
|
|
|
return { x: transformedPoint[0], y: transformedPoint[1] };
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Updates the <canvas> element's style in accordance with the current window's
|
|
* device pixel ratio, and the position calculated in `getCanvasPosition`. It also
|
|
* clears the drawing context. This is called on canvas update after a scroll event where
|
|
* `getCanvasPosition` updates the new canvasPosition.
|
|
*
|
|
* @param {Canvas} canvas
|
|
* The <canvas> element.
|
|
* @param {Object} canvasPosition
|
|
* A pointer object {x, y} representing the <canvas> position to the top left
|
|
* corner of the page.
|
|
* @param {Number} devicePixelRatio
|
|
* The device pixel ratio.
|
|
* @param {Window} [options.zoomWindow]
|
|
* Optional window object used to calculate zoom (default = undefined).
|
|
*/
|
|
function updateCanvasElement(
|
|
canvas,
|
|
canvasPosition,
|
|
devicePixelRatio,
|
|
{ zoomWindow } = {}
|
|
) {
|
|
let { x, y } = canvasPosition;
|
|
const size = CANVAS_SIZE / devicePixelRatio;
|
|
|
|
if (zoomWindow) {
|
|
const zoom = getCurrentZoom(zoomWindow);
|
|
x *= zoom;
|
|
y *= zoom;
|
|
}
|
|
|
|
// Resize the canvas taking the dpr into account so as to have crisp lines, and
|
|
// translating it to give the perception that it always covers the viewport.
|
|
canvas.setAttribute(
|
|
"style",
|
|
`width: ${size}px; height: ${size}px; transform: translate(${x}px, ${y}px);`
|
|
);
|
|
canvas.getCanvasContext("2d").clearRect(0, 0, CANVAS_SIZE, CANVAS_SIZE);
|
|
}
|
|
|
|
/**
|
|
* Calculates and returns the <canvas>'s position in accordance with the page's scroll,
|
|
* document's size, canvas size, and viewport's size. This is called when a page's scroll
|
|
* is detected.
|
|
*
|
|
* @param {Object} canvasPosition
|
|
* A pointer object {x, y} representing the <canvas> position to the top left
|
|
* corner of the page.
|
|
* @param {Object} scrollPosition
|
|
* A pointer object {x, y} representing the window's pageXOffset and pageYOffset.
|
|
* @param {Window} window
|
|
* The window object.
|
|
* @param {Object} windowDimensions
|
|
* An object {width, height} representing the window's dimensions for the
|
|
* `window` given.
|
|
* @return {Boolean} true if the <canvas> position was updated and false otherwise.
|
|
*/
|
|
function updateCanvasPosition(
|
|
canvasPosition,
|
|
scrollPosition,
|
|
window,
|
|
windowDimensions
|
|
) {
|
|
let { x: canvasX, y: canvasY } = canvasPosition;
|
|
const { x: scrollX, y: scrollY } = scrollPosition;
|
|
const cssCanvasSize = CANVAS_SIZE / window.devicePixelRatio;
|
|
const viewportSize = getViewportDimensions(window);
|
|
const { height, width } = windowDimensions;
|
|
const canvasWidth = cssCanvasSize;
|
|
const canvasHeight = cssCanvasSize;
|
|
let hasUpdated = false;
|
|
|
|
// Those values indicates the relative horizontal and vertical space the page can
|
|
// scroll before we have to reposition the <canvas>; they're 1/4 of the delta between
|
|
// the canvas' size and the viewport's size: that's because we want to consider both
|
|
// sides (top/bottom, left/right; so 1/2 for each side) and also we don't want to
|
|
// shown the edges of the canvas in case of fast scrolling (to avoid showing undraw
|
|
// areas, therefore another 1/2 here).
|
|
const bufferSizeX = (canvasWidth - viewportSize.width) >> 2;
|
|
const bufferSizeY = (canvasHeight - viewportSize.height) >> 2;
|
|
|
|
// Defines the boundaries for the canvas.
|
|
const leftBoundary = 0;
|
|
const rightBoundary = width - canvasWidth;
|
|
const topBoundary = 0;
|
|
const bottomBoundary = height - canvasHeight;
|
|
|
|
// Defines the thresholds that triggers the canvas' position to be updated.
|
|
const leftThreshold = scrollX - bufferSizeX;
|
|
const rightThreshold =
|
|
scrollX - canvasWidth + viewportSize.width + bufferSizeX;
|
|
const topThreshold = scrollY - bufferSizeY;
|
|
const bottomThreshold =
|
|
scrollY - canvasHeight + viewportSize.height + bufferSizeY;
|
|
|
|
if (canvasX < rightBoundary && canvasX < rightThreshold) {
|
|
canvasX = Math.min(leftThreshold, rightBoundary);
|
|
hasUpdated = true;
|
|
} else if (canvasX > leftBoundary && canvasX > leftThreshold) {
|
|
canvasX = Math.max(rightThreshold, leftBoundary);
|
|
hasUpdated = true;
|
|
}
|
|
|
|
if (canvasY < bottomBoundary && canvasY < bottomThreshold) {
|
|
canvasY = Math.min(topThreshold, bottomBoundary);
|
|
hasUpdated = true;
|
|
} else if (canvasY > topBoundary && canvasY > topThreshold) {
|
|
canvasY = Math.max(bottomThreshold, topBoundary);
|
|
hasUpdated = true;
|
|
}
|
|
|
|
// Update the canvas position with the calculated canvasX and canvasY positions.
|
|
canvasPosition.x = canvasX;
|
|
canvasPosition.y = canvasY;
|
|
|
|
return hasUpdated;
|
|
}
|
|
|
|
exports.CANVAS_SIZE = CANVAS_SIZE;
|
|
exports.DEFAULT_COLOR = DEFAULT_COLOR;
|
|
exports.clearRect = clearRect;
|
|
exports.drawBubbleRect = drawBubbleRect;
|
|
exports.drawLine = drawLine;
|
|
exports.drawRect = drawRect;
|
|
exports.drawRoundedRect = drawRoundedRect;
|
|
exports.getBoundsFromPoints = getBoundsFromPoints;
|
|
exports.getCurrentMatrix = getCurrentMatrix;
|
|
exports.getPathDescriptionFromPoints = getPathDescriptionFromPoints;
|
|
exports.getPointsFromDiagonal = getPointsFromDiagonal;
|
|
exports.updateCanvasElement = updateCanvasElement;
|
|
exports.updateCanvasPosition = updateCanvasPosition;
|