809 lines
31 KiB
JavaScript
809 lines
31 KiB
JavaScript
import EventEmitter from 'eventemitter3';
|
|
import { Color } from '../../../color/Color.mjs';
|
|
import { Matrix } from '../../../maths/matrix/Matrix.mjs';
|
|
import { Point } from '../../../maths/point/Point.mjs';
|
|
import { Texture } from '../../../rendering/renderers/shared/texture/Texture.mjs';
|
|
import { uid } from '../../../utils/data/uid.mjs';
|
|
import { deprecation, v8_0_0 } from '../../../utils/logging/deprecation.mjs';
|
|
import { Bounds } from '../../container/bounds/Bounds.mjs';
|
|
import { GraphicsPath } from './path/GraphicsPath.mjs';
|
|
import { SVGParser } from './svg/SVGParser.mjs';
|
|
import { toFillStyle, toStrokeStyle } from './utils/convertFillInputToFillStyle.mjs';
|
|
|
|
"use strict";
|
|
const tmpPoint = new Point();
|
|
const tempMatrix = new Matrix();
|
|
const _GraphicsContext = class _GraphicsContext extends EventEmitter {
|
|
constructor() {
|
|
super(...arguments);
|
|
/** unique id for this graphics context */
|
|
this.uid = uid("graphicsContext");
|
|
this.dirty = true;
|
|
this.batchMode = "auto";
|
|
this.instructions = [];
|
|
this._activePath = new GraphicsPath();
|
|
this._transform = new Matrix();
|
|
this._fillStyle = { ..._GraphicsContext.defaultFillStyle };
|
|
this._strokeStyle = { ..._GraphicsContext.defaultStrokeStyle };
|
|
this._stateStack = [];
|
|
this._tick = 0;
|
|
this._bounds = new Bounds();
|
|
this._boundsDirty = true;
|
|
}
|
|
/**
|
|
* Creates a new GraphicsContext object that is a clone of this instance, copying all properties,
|
|
* including the current drawing state, transformations, styles, and instructions.
|
|
* @returns A new GraphicsContext instance with the same properties and state as this one.
|
|
*/
|
|
clone() {
|
|
const clone = new _GraphicsContext();
|
|
clone.batchMode = this.batchMode;
|
|
clone.instructions = this.instructions.slice();
|
|
clone._activePath = this._activePath.clone();
|
|
clone._transform = this._transform.clone();
|
|
clone._fillStyle = { ...this._fillStyle };
|
|
clone._strokeStyle = { ...this._strokeStyle };
|
|
clone._stateStack = this._stateStack.slice();
|
|
clone._bounds = this._bounds.clone();
|
|
clone._boundsDirty = true;
|
|
return clone;
|
|
}
|
|
/**
|
|
* The current fill style of the graphics context. This can be a color, gradient, pattern, or a more complex style defined by a FillStyle object.
|
|
*/
|
|
get fillStyle() {
|
|
return this._fillStyle;
|
|
}
|
|
set fillStyle(value) {
|
|
this._fillStyle = toFillStyle(value, _GraphicsContext.defaultFillStyle);
|
|
}
|
|
/**
|
|
* The current stroke style of the graphics context. Similar to fill styles, stroke styles can encompass colors, gradients, patterns, or more detailed configurations via a StrokeStyle object.
|
|
*/
|
|
get strokeStyle() {
|
|
return this._strokeStyle;
|
|
}
|
|
set strokeStyle(value) {
|
|
this._strokeStyle = toStrokeStyle(value, _GraphicsContext.defaultStrokeStyle);
|
|
}
|
|
/**
|
|
* Sets the current fill style of the graphics context. The fill style can be a color, gradient,
|
|
* pattern, or a more complex style defined by a FillStyle object.
|
|
* @param style - The fill style to apply. This can be a simple color, a gradient or pattern object,
|
|
* or a FillStyle or ConvertedFillStyle object.
|
|
* @returns The instance of the current GraphicsContext for method chaining.
|
|
*/
|
|
setFillStyle(style) {
|
|
this._fillStyle = toFillStyle(style, _GraphicsContext.defaultFillStyle);
|
|
return this;
|
|
}
|
|
/**
|
|
* Sets the current stroke style of the graphics context. Similar to fill styles, stroke styles can
|
|
* encompass colors, gradients, patterns, or more detailed configurations via a StrokeStyle object.
|
|
* @param style - The stroke style to apply. Can be defined as a color, a gradient or pattern,
|
|
* or a StrokeStyle or ConvertedStrokeStyle object.
|
|
* @returns The instance of the current GraphicsContext for method chaining.
|
|
*/
|
|
setStrokeStyle(style) {
|
|
this._strokeStyle = toFillStyle(style, _GraphicsContext.defaultStrokeStyle);
|
|
return this;
|
|
}
|
|
texture(texture, tint, dx, dy, dw, dh) {
|
|
this.instructions.push({
|
|
action: "texture",
|
|
data: {
|
|
image: texture,
|
|
dx: dx || 0,
|
|
dy: dy || 0,
|
|
dw: dw || texture.frame.width,
|
|
dh: dh || texture.frame.height,
|
|
transform: this._transform.clone(),
|
|
alpha: this._fillStyle.alpha,
|
|
style: tint ? Color.shared.setValue(tint).toNumber() : 16777215
|
|
}
|
|
});
|
|
this.onUpdate();
|
|
return this;
|
|
}
|
|
/**
|
|
* Resets the current path. Any previous path and its commands are discarded and a new path is
|
|
* started. This is typically called before beginning a new shape or series of drawing commands.
|
|
* @returns The instance of the current GraphicsContext for method chaining.
|
|
*/
|
|
beginPath() {
|
|
this._activePath = new GraphicsPath();
|
|
return this;
|
|
}
|
|
fill(style, alpha) {
|
|
let path;
|
|
const lastInstruction = this.instructions[this.instructions.length - 1];
|
|
if (this._tick === 0 && lastInstruction && lastInstruction.action === "stroke") {
|
|
path = lastInstruction.data.path;
|
|
} else {
|
|
path = this._activePath.clone();
|
|
}
|
|
if (!path)
|
|
return this;
|
|
if (style != null) {
|
|
if (alpha !== void 0 && typeof style === "number") {
|
|
deprecation(v8_0_0, "GraphicsContext.fill(color, alpha) is deprecated, use GraphicsContext.fill({ color, alpha }) instead");
|
|
style = { color: style, alpha };
|
|
}
|
|
this._fillStyle = toFillStyle(style, _GraphicsContext.defaultFillStyle);
|
|
}
|
|
this.instructions.push({
|
|
action: "fill",
|
|
// TODO copy fill style!
|
|
data: { style: this.fillStyle, path }
|
|
});
|
|
this.onUpdate();
|
|
this._initNextPathLocation();
|
|
this._tick = 0;
|
|
return this;
|
|
}
|
|
_initNextPathLocation() {
|
|
const { x, y } = this._activePath.getLastPoint(Point.shared);
|
|
this._activePath.clear();
|
|
this._activePath.moveTo(x, y);
|
|
}
|
|
/**
|
|
* Strokes the current path with the current stroke style. This method can take an optional
|
|
* FillInput parameter to define the stroke's appearance, including its color, width, and other properties.
|
|
* @param style - (Optional) The stroke style to apply. Can be defined as a simple color or a more complex style object. If omitted, uses the current stroke style.
|
|
* @returns The instance of the current GraphicsContext for method chaining.
|
|
*/
|
|
stroke(style) {
|
|
let path;
|
|
const lastInstruction = this.instructions[this.instructions.length - 1];
|
|
if (this._tick === 0 && lastInstruction && lastInstruction.action === "fill") {
|
|
path = lastInstruction.data.path;
|
|
} else {
|
|
path = this._activePath.clone();
|
|
}
|
|
if (!path)
|
|
return this;
|
|
if (style != null) {
|
|
this._strokeStyle = toStrokeStyle(style, _GraphicsContext.defaultStrokeStyle);
|
|
}
|
|
this.instructions.push({
|
|
action: "stroke",
|
|
// TODO copy fill style!
|
|
data: { style: this.strokeStyle, path }
|
|
});
|
|
this.onUpdate();
|
|
this._initNextPathLocation();
|
|
this._tick = 0;
|
|
return this;
|
|
}
|
|
/**
|
|
* Applies a cutout to the last drawn shape. This is used to create holes or complex shapes by
|
|
* subtracting a path from the previously drawn path. If a hole is not completely in a shape, it will
|
|
* fail to cut correctly!
|
|
* @returns The instance of the current GraphicsContext for method chaining.
|
|
*/
|
|
cut() {
|
|
for (let i = 0; i < 2; i++) {
|
|
const lastInstruction = this.instructions[this.instructions.length - 1 - i];
|
|
const holePath = this._activePath.clone();
|
|
if (lastInstruction) {
|
|
if (lastInstruction.action === "stroke" || lastInstruction.action === "fill") {
|
|
if (lastInstruction.data.hole) {
|
|
lastInstruction.data.hole.addPath(holePath);
|
|
} else {
|
|
lastInstruction.data.hole = holePath;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
this._initNextPathLocation();
|
|
return this;
|
|
}
|
|
/**
|
|
* Adds an arc to the current path, which is centered at (x, y) with the specified radius,
|
|
* starting and ending angles, and direction.
|
|
* @param x - The x-coordinate of the arc's center.
|
|
* @param y - The y-coordinate of the arc's center.
|
|
* @param radius - The arc's radius.
|
|
* @param startAngle - The starting angle, in radians.
|
|
* @param endAngle - The ending angle, in radians.
|
|
* @param counterclockwise - (Optional) Specifies whether the arc is drawn counterclockwise (true) or clockwise (false). Defaults to false.
|
|
* @returns The instance of the current GraphicsContext for method chaining.
|
|
*/
|
|
arc(x, y, radius, startAngle, endAngle, counterclockwise) {
|
|
this._tick++;
|
|
const t = this._transform;
|
|
this._activePath.arc(
|
|
t.a * x + t.c * y + t.tx,
|
|
t.b * x + t.d * y + t.ty,
|
|
radius,
|
|
startAngle,
|
|
endAngle,
|
|
counterclockwise
|
|
);
|
|
return this;
|
|
}
|
|
/**
|
|
* Adds an arc to the current path with the given control points and radius, connected to the previous point
|
|
* by a straight line if necessary.
|
|
* @param x1 - The x-coordinate of the first control point.
|
|
* @param y1 - The y-coordinate of the first control point.
|
|
* @param x2 - The x-coordinate of the second control point.
|
|
* @param y2 - The y-coordinate of the second control point.
|
|
* @param radius - The arc's radius.
|
|
* @returns The instance of the current GraphicsContext for method chaining.
|
|
*/
|
|
arcTo(x1, y1, x2, y2, radius) {
|
|
this._tick++;
|
|
const t = this._transform;
|
|
this._activePath.arcTo(
|
|
t.a * x1 + t.c * y1 + t.tx,
|
|
t.b * x1 + t.d * y1 + t.ty,
|
|
t.a * x2 + t.c * y2 + t.tx,
|
|
t.b * x2 + t.d * y2 + t.ty,
|
|
radius
|
|
);
|
|
return this;
|
|
}
|
|
/**
|
|
* Adds an SVG-style arc to the path, allowing for elliptical arcs based on the SVG spec.
|
|
* @param rx - The x-radius of the ellipse.
|
|
* @param ry - The y-radius of the ellipse.
|
|
* @param xAxisRotation - The rotation of the ellipse's x-axis relative
|
|
* to the x-axis of the coordinate system, in degrees.
|
|
* @param largeArcFlag - Determines if the arc should be greater than or less than 180 degrees.
|
|
* @param sweepFlag - Determines if the arc should be swept in a positive angle direction.
|
|
* @param x - The x-coordinate of the arc's end point.
|
|
* @param y - The y-coordinate of the arc's end point.
|
|
* @returns The instance of the current object for chaining.
|
|
*/
|
|
arcToSvg(rx, ry, xAxisRotation, largeArcFlag, sweepFlag, x, y) {
|
|
this._tick++;
|
|
const t = this._transform;
|
|
this._activePath.arcToSvg(
|
|
rx,
|
|
ry,
|
|
xAxisRotation,
|
|
// should we rotate this with transform??
|
|
largeArcFlag,
|
|
sweepFlag,
|
|
t.a * x + t.c * y + t.tx,
|
|
t.b * x + t.d * y + t.ty
|
|
);
|
|
return this;
|
|
}
|
|
/**
|
|
* Adds a cubic Bezier curve to the path.
|
|
* It requires three points: the first two are control points and the third one is the end point.
|
|
* The starting point is the last point in the current path.
|
|
* @param cp1x - The x-coordinate of the first control point.
|
|
* @param cp1y - The y-coordinate of the first control point.
|
|
* @param cp2x - The x-coordinate of the second control point.
|
|
* @param cp2y - The y-coordinate of the second control point.
|
|
* @param x - The x-coordinate of the end point.
|
|
* @param y - The y-coordinate of the end point.
|
|
* @param smoothness - Optional parameter to adjust the smoothness of the curve.
|
|
* @returns The instance of the current object for chaining.
|
|
*/
|
|
bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y, smoothness) {
|
|
this._tick++;
|
|
const t = this._transform;
|
|
this._activePath.bezierCurveTo(
|
|
t.a * cp1x + t.c * cp1y + t.tx,
|
|
t.b * cp1x + t.d * cp1y + t.ty,
|
|
t.a * cp2x + t.c * cp2y + t.tx,
|
|
t.b * cp2x + t.d * cp2y + t.ty,
|
|
t.a * x + t.c * y + t.tx,
|
|
t.b * x + t.d * y + t.ty,
|
|
smoothness
|
|
);
|
|
return this;
|
|
}
|
|
/**
|
|
* Closes the current path by drawing a straight line back to the start.
|
|
* If the shape is already closed or there are no points in the path, this method does nothing.
|
|
* @returns The instance of the current object for chaining.
|
|
*/
|
|
closePath() {
|
|
this._tick++;
|
|
this._activePath?.closePath();
|
|
return this;
|
|
}
|
|
/**
|
|
* Draws an ellipse at the specified location and with the given x and y radii.
|
|
* An optional transformation can be applied, allowing for rotation, scaling, and translation.
|
|
* @param x - The x-coordinate of the center of the ellipse.
|
|
* @param y - The y-coordinate of the center of the ellipse.
|
|
* @param radiusX - The horizontal radius of the ellipse.
|
|
* @param radiusY - The vertical radius of the ellipse.
|
|
* @returns The instance of the current object for chaining.
|
|
*/
|
|
ellipse(x, y, radiusX, radiusY) {
|
|
this._tick++;
|
|
this._activePath.ellipse(x, y, radiusX, radiusY, this._transform.clone());
|
|
return this;
|
|
}
|
|
/**
|
|
* Draws a circle shape. This method adds a new circle path to the current drawing.
|
|
* @param x - The x-coordinate of the center of the circle.
|
|
* @param y - The y-coordinate of the center of the circle.
|
|
* @param radius - The radius of the circle.
|
|
* @returns The instance of the current object for chaining.
|
|
*/
|
|
circle(x, y, radius) {
|
|
this._tick++;
|
|
this._activePath.circle(x, y, radius, this._transform.clone());
|
|
return this;
|
|
}
|
|
/**
|
|
* Adds another `GraphicsPath` to this path, optionally applying a transformation.
|
|
* @param path - The `GraphicsPath` to add.
|
|
* @returns The instance of the current object for chaining.
|
|
*/
|
|
path(path) {
|
|
this._tick++;
|
|
this._activePath.addPath(path, this._transform.clone());
|
|
return this;
|
|
}
|
|
/**
|
|
* Connects the current point to a new point with a straight line. This method updates the current path.
|
|
* @param x - The x-coordinate of the new point to connect to.
|
|
* @param y - The y-coordinate of the new point to connect to.
|
|
* @returns The instance of the current object for chaining.
|
|
*/
|
|
lineTo(x, y) {
|
|
this._tick++;
|
|
const t = this._transform;
|
|
this._activePath.lineTo(
|
|
t.a * x + t.c * y + t.tx,
|
|
t.b * x + t.d * y + t.ty
|
|
);
|
|
return this;
|
|
}
|
|
/**
|
|
* Sets the starting point for a new sub-path. Any subsequent drawing commands are considered part of this path.
|
|
* @param x - The x-coordinate for the starting point.
|
|
* @param y - The y-coordinate for the starting point.
|
|
* @returns The instance of the current object for chaining.
|
|
*/
|
|
moveTo(x, y) {
|
|
this._tick++;
|
|
const t = this._transform;
|
|
const instructions = this._activePath.instructions;
|
|
const transformedX = t.a * x + t.c * y + t.tx;
|
|
const transformedY = t.b * x + t.d * y + t.ty;
|
|
if (instructions.length === 1 && instructions[0].action === "moveTo") {
|
|
instructions[0].data[0] = transformedX;
|
|
instructions[0].data[1] = transformedY;
|
|
return this;
|
|
}
|
|
this._activePath.moveTo(
|
|
transformedX,
|
|
transformedY
|
|
);
|
|
return this;
|
|
}
|
|
/**
|
|
* Adds a quadratic curve to the path. It requires two points: the control point and the end point.
|
|
* The starting point is the last point in the current path.
|
|
* @param cpx - The x-coordinate of the control point.
|
|
* @param cpy - The y-coordinate of the control point.
|
|
* @param x - The x-coordinate of the end point.
|
|
* @param y - The y-coordinate of the end point.
|
|
* @param smoothness - Optional parameter to adjust the smoothness of the curve.
|
|
* @returns The instance of the current object for chaining.
|
|
*/
|
|
quadraticCurveTo(cpx, cpy, x, y, smoothness) {
|
|
this._tick++;
|
|
const t = this._transform;
|
|
this._activePath.quadraticCurveTo(
|
|
t.a * cpx + t.c * cpy + t.tx,
|
|
t.b * cpx + t.d * cpy + t.ty,
|
|
t.a * x + t.c * y + t.tx,
|
|
t.b * x + t.d * y + t.ty,
|
|
smoothness
|
|
);
|
|
return this;
|
|
}
|
|
/**
|
|
* Draws a rectangle shape. This method adds a new rectangle path to the current drawing.
|
|
* @param x - The x-coordinate of the top-left corner of the rectangle.
|
|
* @param y - The y-coordinate of the top-left corner of the rectangle.
|
|
* @param w - The width of the rectangle.
|
|
* @param h - The height of the rectangle.
|
|
* @returns The instance of the current object for chaining.
|
|
*/
|
|
rect(x, y, w, h) {
|
|
this._tick++;
|
|
this._activePath.rect(x, y, w, h, this._transform.clone());
|
|
return this;
|
|
}
|
|
/**
|
|
* Draws a rectangle with rounded corners.
|
|
* The corner radius can be specified to determine how rounded the corners should be.
|
|
* An optional transformation can be applied, which allows for rotation, scaling, and translation of the rectangle.
|
|
* @param x - The x-coordinate of the top-left corner of the rectangle.
|
|
* @param y - The y-coordinate of the top-left corner of the rectangle.
|
|
* @param w - The width of the rectangle.
|
|
* @param h - The height of the rectangle.
|
|
* @param radius - The radius of the rectangle's corners. If not specified, corners will be sharp.
|
|
* @returns The instance of the current object for chaining.
|
|
*/
|
|
roundRect(x, y, w, h, radius) {
|
|
this._tick++;
|
|
this._activePath.roundRect(x, y, w, h, radius, this._transform.clone());
|
|
return this;
|
|
}
|
|
/**
|
|
* Draws a polygon shape by specifying a sequence of points. This method allows for the creation of complex polygons,
|
|
* which can be both open and closed. An optional transformation can be applied, enabling the polygon to be scaled,
|
|
* rotated, or translated as needed.
|
|
* @param points - An array of numbers, or an array of PointData objects eg [{x,y}, {x,y}, {x,y}]
|
|
* representing the x and y coordinates, of the polygon's vertices, in sequence.
|
|
* @param close - A boolean indicating whether to close the polygon path. True by default.
|
|
*/
|
|
poly(points, close) {
|
|
this._tick++;
|
|
this._activePath.poly(points, close, this._transform.clone());
|
|
return this;
|
|
}
|
|
/**
|
|
* Draws a regular polygon with a specified number of sides. All sides and angles are equal.
|
|
* @param x - The x-coordinate of the center of the polygon.
|
|
* @param y - The y-coordinate of the center of the polygon.
|
|
* @param radius - The radius of the circumscribed circle of the polygon.
|
|
* @param sides - The number of sides of the polygon. Must be 3 or more.
|
|
* @param rotation - The rotation angle of the polygon, in radians. Zero by default.
|
|
* @param transform - An optional `Matrix` object to apply a transformation to the polygon.
|
|
* @returns The instance of the current object for chaining.
|
|
*/
|
|
regularPoly(x, y, radius, sides, rotation = 0, transform) {
|
|
this._tick++;
|
|
this._activePath.regularPoly(x, y, radius, sides, rotation, transform);
|
|
return this;
|
|
}
|
|
/**
|
|
* Draws a polygon with rounded corners.
|
|
* Similar to `regularPoly` but with the ability to round the corners of the polygon.
|
|
* @param x - The x-coordinate of the center of the polygon.
|
|
* @param y - The y-coordinate of the center of the polygon.
|
|
* @param radius - The radius of the circumscribed circle of the polygon.
|
|
* @param sides - The number of sides of the polygon. Must be 3 or more.
|
|
* @param corner - The radius of the rounding of the corners.
|
|
* @param rotation - The rotation angle of the polygon, in radians. Zero by default.
|
|
* @returns The instance of the current object for chaining.
|
|
*/
|
|
roundPoly(x, y, radius, sides, corner, rotation) {
|
|
this._tick++;
|
|
this._activePath.roundPoly(x, y, radius, sides, corner, rotation);
|
|
return this;
|
|
}
|
|
/**
|
|
* Draws a shape with rounded corners. This function supports custom radius for each corner of the shape.
|
|
* Optionally, corners can be rounded using a quadratic curve instead of an arc, providing a different aesthetic.
|
|
* @param points - An array of `RoundedPoint` representing the corners of the shape to draw.
|
|
* A minimum of 3 points is required.
|
|
* @param radius - The default radius for the corners.
|
|
* This radius is applied to all corners unless overridden in `points`.
|
|
* @param useQuadratic - If set to true, rounded corners are drawn using a quadraticCurve
|
|
* method instead of an arc method. Defaults to false.
|
|
* @param smoothness - Specifies the smoothness of the curve when `useQuadratic` is true.
|
|
* Higher values make the curve smoother.
|
|
* @returns The instance of the current object for chaining.
|
|
*/
|
|
roundShape(points, radius, useQuadratic, smoothness) {
|
|
this._tick++;
|
|
this._activePath.roundShape(points, radius, useQuadratic, smoothness);
|
|
return this;
|
|
}
|
|
/**
|
|
* Draw Rectangle with fillet corners. This is much like rounded rectangle
|
|
* however it support negative numbers as well for the corner radius.
|
|
* @param x - Upper left corner of rect
|
|
* @param y - Upper right corner of rect
|
|
* @param width - Width of rect
|
|
* @param height - Height of rect
|
|
* @param fillet - accept negative or positive values
|
|
*/
|
|
filletRect(x, y, width, height, fillet) {
|
|
this._tick++;
|
|
this._activePath.filletRect(x, y, width, height, fillet);
|
|
return this;
|
|
}
|
|
/**
|
|
* Draw Rectangle with chamfer corners. These are angled corners.
|
|
* @param x - Upper left corner of rect
|
|
* @param y - Upper right corner of rect
|
|
* @param width - Width of rect
|
|
* @param height - Height of rect
|
|
* @param chamfer - non-zero real number, size of corner cutout
|
|
* @param transform
|
|
*/
|
|
chamferRect(x, y, width, height, chamfer, transform) {
|
|
this._tick++;
|
|
this._activePath.chamferRect(x, y, width, height, chamfer, transform);
|
|
return this;
|
|
}
|
|
/**
|
|
* Draws a star shape centered at a specified location. This method allows for the creation
|
|
* of stars with a variable number of points, outer radius, optional inner radius, and rotation.
|
|
* The star is drawn as a closed polygon with alternating outer and inner vertices to create the star's points.
|
|
* An optional transformation can be applied to scale, rotate, or translate the star as needed.
|
|
* @param x - The x-coordinate of the center of the star.
|
|
* @param y - The y-coordinate of the center of the star.
|
|
* @param points - The number of points of the star.
|
|
* @param radius - The outer radius of the star (distance from the center to the outer points).
|
|
* @param innerRadius - Optional. The inner radius of the star
|
|
* (distance from the center to the inner points between the outer points).
|
|
* If not provided, defaults to half of the `radius`.
|
|
* @param rotation - Optional. The rotation of the star in radians, where 0 is aligned with the y-axis.
|
|
* Defaults to 0, meaning one point is directly upward.
|
|
* @returns The instance of the current object for chaining further drawing commands.
|
|
*/
|
|
star(x, y, points, radius, innerRadius = 0, rotation = 0) {
|
|
this._tick++;
|
|
this._activePath.star(x, y, points, radius, innerRadius, rotation, this._transform.clone());
|
|
return this;
|
|
}
|
|
/**
|
|
* Parses and renders an SVG string into the graphics context. This allows for complex shapes and paths
|
|
* defined in SVG format to be drawn within the graphics context.
|
|
* @param svg - The SVG string to be parsed and rendered.
|
|
*/
|
|
svg(svg) {
|
|
this._tick++;
|
|
SVGParser(svg, this);
|
|
return this;
|
|
}
|
|
/**
|
|
* Restores the most recently saved graphics state by popping the top of the graphics state stack.
|
|
* This includes transformations, fill styles, and stroke styles.
|
|
*/
|
|
restore() {
|
|
const state = this._stateStack.pop();
|
|
if (state) {
|
|
this._transform = state.transform;
|
|
this._fillStyle = state.fillStyle;
|
|
this._strokeStyle = state.strokeStyle;
|
|
}
|
|
return this;
|
|
}
|
|
/** Saves the current graphics state, including transformations, fill styles, and stroke styles, onto a stack. */
|
|
save() {
|
|
this._stateStack.push({
|
|
transform: this._transform.clone(),
|
|
fillStyle: { ...this._fillStyle },
|
|
strokeStyle: { ...this._strokeStyle }
|
|
});
|
|
return this;
|
|
}
|
|
/**
|
|
* Returns the current transformation matrix of the graphics context.
|
|
* @returns The current transformation matrix.
|
|
*/
|
|
getTransform() {
|
|
return this._transform;
|
|
}
|
|
/**
|
|
* Resets the current transformation matrix to the identity matrix, effectively removing any transformations (rotation, scaling, translation) previously applied.
|
|
* @returns The instance of the current GraphicsContext for method chaining.
|
|
*/
|
|
resetTransform() {
|
|
this._transform.identity();
|
|
return this;
|
|
}
|
|
/**
|
|
* Applies a rotation transformation to the graphics context around the current origin.
|
|
* @param angle - The angle of rotation in radians.
|
|
* @returns The instance of the current GraphicsContext for method chaining.
|
|
*/
|
|
rotate(angle) {
|
|
this._transform.rotate(angle);
|
|
return this;
|
|
}
|
|
/**
|
|
* Applies a scaling transformation to the graphics context, scaling drawings by x horizontally and by y vertically.
|
|
* @param x - The scale factor in the horizontal direction.
|
|
* @param y - (Optional) The scale factor in the vertical direction. If not specified, the x value is used for both directions.
|
|
* @returns The instance of the current GraphicsContext for method chaining.
|
|
*/
|
|
scale(x, y = x) {
|
|
this._transform.scale(x, y);
|
|
return this;
|
|
}
|
|
setTransform(a, b, c, d, dx, dy) {
|
|
if (a instanceof Matrix) {
|
|
this._transform.set(a.a, a.b, a.c, a.d, a.tx, a.ty);
|
|
return this;
|
|
}
|
|
this._transform.set(a, b, c, d, dx, dy);
|
|
return this;
|
|
}
|
|
transform(a, b, c, d, dx, dy) {
|
|
if (a instanceof Matrix) {
|
|
this._transform.append(a);
|
|
return this;
|
|
}
|
|
tempMatrix.set(a, b, c, d, dx, dy);
|
|
this._transform.append(tempMatrix);
|
|
return this;
|
|
}
|
|
/**
|
|
* Applies a translation transformation to the graphics context, moving the origin by the specified amounts.
|
|
* @param x - The amount to translate in the horizontal direction.
|
|
* @param y - (Optional) The amount to translate in the vertical direction. If not specified, the x value is used for both directions.
|
|
* @returns The instance of the current GraphicsContext for method chaining.
|
|
*/
|
|
translate(x, y = x) {
|
|
this._transform.translate(x, y);
|
|
return this;
|
|
}
|
|
/**
|
|
* Clears all drawing commands from the graphics context, effectively resetting it. This includes clearing the path,
|
|
* and optionally resetting transformations to the identity matrix.
|
|
* @returns The instance of the current GraphicsContext for method chaining.
|
|
*/
|
|
clear() {
|
|
this._activePath.clear();
|
|
this.instructions.length = 0;
|
|
this.resetTransform();
|
|
this.onUpdate();
|
|
return this;
|
|
}
|
|
onUpdate() {
|
|
if (this.dirty)
|
|
return;
|
|
this.emit("update", this, 16);
|
|
this.dirty = true;
|
|
this._boundsDirty = true;
|
|
}
|
|
/** The bounds of the graphic shape. */
|
|
get bounds() {
|
|
if (!this._boundsDirty)
|
|
return this._bounds;
|
|
const bounds = this._bounds;
|
|
bounds.clear();
|
|
for (let i = 0; i < this.instructions.length; i++) {
|
|
const instruction = this.instructions[i];
|
|
const action = instruction.action;
|
|
if (action === "fill") {
|
|
const data = instruction.data;
|
|
bounds.addBounds(data.path.bounds);
|
|
} else if (action === "texture") {
|
|
const data = instruction.data;
|
|
bounds.addFrame(data.dx, data.dy, data.dx + data.dw, data.dy + data.dh, data.transform);
|
|
}
|
|
if (action === "stroke") {
|
|
const data = instruction.data;
|
|
const padding = data.style.width / 2;
|
|
const _bounds = data.path.bounds;
|
|
bounds.addFrame(
|
|
_bounds.minX - padding,
|
|
_bounds.minY - padding,
|
|
_bounds.maxX + padding,
|
|
_bounds.maxY + padding
|
|
);
|
|
}
|
|
}
|
|
return bounds;
|
|
}
|
|
/**
|
|
* Check to see if a point is contained within this geometry.
|
|
* @param point - Point to check if it's contained.
|
|
* @returns {boolean} `true` if the point is contained within geometry.
|
|
*/
|
|
containsPoint(point) {
|
|
if (!this.bounds.containsPoint(point.x, point.y))
|
|
return false;
|
|
const instructions = this.instructions;
|
|
let hasHit = false;
|
|
for (let k = 0; k < instructions.length; k++) {
|
|
const instruction = instructions[k];
|
|
const data = instruction.data;
|
|
const path = data.path;
|
|
if (!instruction.action || !path)
|
|
continue;
|
|
const style = data.style;
|
|
const shapes = path.shapePath.shapePrimitives;
|
|
for (let i = 0; i < shapes.length; i++) {
|
|
const shape = shapes[i].shape;
|
|
if (!style || !shape)
|
|
continue;
|
|
const transform = shapes[i].transform;
|
|
const transformedPoint = transform ? transform.applyInverse(point, tmpPoint) : point;
|
|
if (instruction.action === "fill") {
|
|
hasHit = shape.contains(transformedPoint.x, transformedPoint.y);
|
|
} else {
|
|
hasHit = shape.strokeContains(transformedPoint.x, transformedPoint.y, style.width);
|
|
}
|
|
const holes = data.hole;
|
|
if (holes) {
|
|
const holeShapes = holes.shapePath?.shapePrimitives;
|
|
if (holeShapes) {
|
|
for (let j = 0; j < holeShapes.length; j++) {
|
|
if (holeShapes[j].shape.contains(transformedPoint.x, transformedPoint.y)) {
|
|
hasHit = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (hasHit) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return hasHit;
|
|
}
|
|
/**
|
|
* Destroys the GraphicsData object.
|
|
* @param options - Options parameter. A boolean will act as if all options
|
|
* have been set to that value
|
|
* @param {boolean} [options.texture=false] - Should it destroy the current texture of the fill/stroke style?
|
|
* @param {boolean} [options.textureSource=false] - Should it destroy the texture source of the fill/stroke style?
|
|
*/
|
|
destroy(options = false) {
|
|
this._stateStack.length = 0;
|
|
this._transform = null;
|
|
this.emit("destroy", this);
|
|
this.removeAllListeners();
|
|
const destroyTexture = typeof options === "boolean" ? options : options?.texture;
|
|
if (destroyTexture) {
|
|
const destroyTextureSource = typeof options === "boolean" ? options : options?.textureSource;
|
|
if (this._fillStyle.texture) {
|
|
this._fillStyle.texture.destroy(destroyTextureSource);
|
|
}
|
|
if (this._strokeStyle.texture) {
|
|
this._strokeStyle.texture.destroy(destroyTextureSource);
|
|
}
|
|
}
|
|
this._fillStyle = null;
|
|
this._strokeStyle = null;
|
|
this.instructions = null;
|
|
this._activePath = null;
|
|
this._bounds = null;
|
|
this._stateStack = null;
|
|
this.customShader = null;
|
|
this._transform = null;
|
|
}
|
|
};
|
|
/** The default fill style to use when none is provided. */
|
|
_GraphicsContext.defaultFillStyle = {
|
|
/** The color to use for the fill. */
|
|
color: 16777215,
|
|
/** The alpha value to use for the fill. */
|
|
alpha: 1,
|
|
/** The texture to use for the fill. */
|
|
texture: Texture.WHITE,
|
|
/** The matrix to apply. */
|
|
matrix: null,
|
|
/** The fill pattern to use. */
|
|
fill: null
|
|
};
|
|
/** The default stroke style to use when none is provided. */
|
|
_GraphicsContext.defaultStrokeStyle = {
|
|
/** The width of the stroke. */
|
|
width: 1,
|
|
/** The color to use for the stroke. */
|
|
color: 16777215,
|
|
/** The alpha value to use for the stroke. */
|
|
alpha: 1,
|
|
/** The alignment of the stroke. */
|
|
alignment: 0.5,
|
|
/** The miter limit to use. */
|
|
miterLimit: 10,
|
|
/** The line cap style to use. */
|
|
cap: "butt",
|
|
/** The line join style to use. */
|
|
join: "miter",
|
|
/** The texture to use for the fill. */
|
|
texture: Texture.WHITE,
|
|
/** The matrix to apply. */
|
|
matrix: null,
|
|
/** The fill pattern to use. */
|
|
fill: null
|
|
};
|
|
let GraphicsContext = _GraphicsContext;
|
|
|
|
export { GraphicsContext };
|
|
//# sourceMappingURL=GraphicsContext.mjs.map
|