UNPKG

@awayjs/graphics

Version:
342 lines (340 loc) 14.8 kB
import { Box } from '@awayjs/core'; import { GraphicsPathWinding } from '../draw/GraphicsPathWinding'; import { GraphicsPathCommand } from '../draw/GraphicsPathCommand'; import { GraphicsFillStyle } from '../draw/GraphicsFillStyle'; import { GraphicsStrokeStyle } from '../draw/GraphicsStrokeStyle'; import { GraphicsFactoryHelper } from '../draw/GraphicsFactoryHelper'; import { Settings } from '../Settings'; /** * Defines the values to use for specifying path-drawing commands. * The values in this class are used by the Graphics.drawPath() method, *or stored in the commands vector of a GraphicsPath object. */ var GraphicsPath = /** @class */ (function () { function GraphicsPath( /** * The Vector of drawing commands as integers representing the path. */ commands, /** * The Vector of numbers containing the parameters used with the drawing commands. */ data, /** * Specifies the winding rule using a value defined in the GraphicsPathWinding class. */ winding) { if (commands === void 0) { commands = []; } if (data === void 0) { data = []; } if (winding === void 0) { winding = GraphicsPathWinding.EVEN_ODD; } this.commands = commands; this.data = data; this.winding = winding; this._orientedBoxBoundsDirty = true; this._lastPrepareScale = -1; /** * When path is morph, we can't filtrate commands */ this.morphSource = false; /** * The Vector of Numbers containing the parameters used with the drawing commands. */ this._positions = []; this._verts = []; this._lastDirtyID = 0; this._dirtyID = -1; this.forceClose = false; } Object.defineProperty(GraphicsPath.prototype, "verts", { /** * The Vector of Numbers containing the parameters used with the drawing commands. */ get: function () { return this._verts; }, set: function (v) { this._verts = v; }, enumerable: false, configurable: true }); Object.defineProperty(GraphicsPath.prototype, "dirty", { get: function () { return this._lastDirtyID !== this._dirtyID; }, enumerable: false, configurable: true }); Object.defineProperty(GraphicsPath.prototype, "data_type", { get: function () { return GraphicsPath.data_type; }, enumerable: false, configurable: true }); Object.defineProperty(GraphicsPath.prototype, "style", { get: function () { return this._style; }, set: function (value) { this._style = value; this._dirtyID++; }, enumerable: false, configurable: true }); Object.defineProperty(GraphicsPath.prototype, "fill", { get: function () { if (this._style == null) return null; if (this._style.data_type == GraphicsFillStyle.data_type) return this._style; return null; }, enumerable: false, configurable: true }); Object.defineProperty(GraphicsPath.prototype, "stroke", { get: function () { if (this._style == null) return null; if (this._style.data_type == GraphicsStrokeStyle.data_type) { return this._style; } return null; }, enumerable: false, configurable: true }); GraphicsPath.prototype.curveTo = function (controlX, controlY, anchorX, anchorY) { this.commands.push(GraphicsPathCommand.CURVE_TO); this.data.push(controlX, controlY, anchorX, anchorY); this._dirtyID++; }; GraphicsPath.prototype.cubicCurveTo = function (controlX, controlY, control2X, control2Y, anchorX, anchorY) { this.commands.push(GraphicsPathCommand.CUBIC_CURVE); this.data.push(controlX, controlY, control2X, control2Y, anchorX, anchorY); this._dirtyID++; }; GraphicsPath.prototype.lineTo = function (x, y) { this.commands.push(GraphicsPathCommand.LINE_TO); this.data.push(x, y); this._dirtyID++; }; GraphicsPath.prototype.moveTo = function (x, y) { this.commands.push(GraphicsPathCommand.MOVE_TO); this.data.push(x, y); this._dirtyID++; }; GraphicsPath.prototype.wideLineTo = function (_x, _y) { }; GraphicsPath.prototype.wideMoveTo = function (_x, _y) { }; /** * * @param qualityScale change a pretesselation quality, > 1 decrease limits * shape will have more vertices, < 1 >0, reduce vertices - bad scale ratio */ GraphicsPath.prototype.prepare = function (qualityScale) { if (qualityScale === void 0) { qualityScale = 1; } // was not mutated internaly if (this._dirtyID === this._lastDirtyID && qualityScale === this._lastPrepareScale) { return; } this._lastPrepareScale = qualityScale; this._lastDirtyID = this._dirtyID; var len = this.commands.length; // commands may be empty if (len === 1 && !this.commands[0]) return false; var eps = 1 / (100 * qualityScale); var commands = this.commands; var data = this.data; var positions = this._positions = []; // now we collect the final position data // a command list is no longer needed for this position data, // we resolve all curves to line segments here var contour, prev_x, prev_y, ctrl_x, ctrl_y, ctrl_x2, ctrl_y2, end_x, end_y; var d = 0, p = 0; // If we don't start with a moveTo command, ensure origin is added to positions if (commands[0] != GraphicsPathCommand.MOVE_TO) { positions[p++] = contour = [prev_x = 0, prev_y = 0]; } for (var c = 0; c < len; c++) { switch (commands[c]) { case GraphicsPathCommand.MOVE_TO: if (c) { //overwrite last command if it was a moveTo if (contour.length == 1) { positions[p] = contour = [prev_x = data[d++], prev_y = data[d++]]; break; } // check if the last contour is closed. // if its not closed, we optionally close it by adding the first point to the end of the contour if (this.forceClose && Math.abs(contour[0] - contour[contour.length - 2]) + Math.abs(contour[1] - contour[contour.length - 1]) > eps) contour.push(contour[0], contour[1]); } positions[p++] = contour = [prev_x = data[d++], prev_y = data[d++]]; break; case GraphicsPathCommand.LINE_TO: end_x = data[d++]; end_y = data[d++]; if (this._minimumCheck(prev_x - end_x, prev_y - end_y)) break; contour.push(prev_x = end_x, prev_y = end_y); break; case GraphicsPathCommand.CURVE_TO: ctrl_x = data[d++]; ctrl_y = data[d++]; end_x = data[d++]; end_y = data[d++]; if (this._minimumCheck(ctrl_x - end_x, ctrl_y - end_y)) { // if all points are less than miniumum draw distance, ignore if (this._minimumCheck(prev_x - end_x, prev_y - end_y)) break; //if control is end, substitute lineTo command contour.push(prev_x = end_x, prev_y = end_y); break; } else if (this._minimumCheck(prev_x - ctrl_x, prev_y - ctrl_y)) { //if prev point is control, substitute lineTo command contour.push(prev_x = end_x, prev_y = end_y); break; } GraphicsFactoryHelper.tesselateCurve(prev_x, prev_y, ctrl_x, ctrl_y, prev_x = end_x, prev_y = end_y, contour, false, 0, qualityScale); break; case GraphicsPathCommand.CUBIC_CURVE: ctrl_x = data[d++]; ctrl_y = data[d++]; ctrl_x2 = data[d++]; ctrl_y2 = data[d++]; end_x = data[d++]; end_y = data[d++]; if (this._minimumCheck(ctrl_x2 - end_x, ctrl_y2 - end_y)) { if (this._minimumCheck(ctrl_x - end_x, ctrl_y - end_y)) { // if all points are less than miniumum draw distance, ignore if (this._minimumCheck(prev_x - end_x, prev_y - end_y)) break; //if control and control2 are end, substitute lineTo command contour.push(prev_x = end_x, prev_y = end_y); break; } else if (this._minimumCheck(prev_x - ctrl_x, prev_y - ctrl_y)) { //if prev point is control and control2 is end, substitute lineTo command contour.push(prev_x = end_x, prev_y = end_y); break; } //if control2 is end substitute curveTo command GraphicsFactoryHelper.tesselateCurve(prev_x, prev_y, ctrl_x, ctrl_y, prev_x = end_x, prev_y = end_y, contour, false, 0, qualityScale); break; } else if (this._minimumCheck(ctrl_x - ctrl_x2, ctrl_y - ctrl_y2)) { if (this._minimumCheck(prev_x - ctrl_x2, prev_y - ctrl_y2)) { //if prev point and control are control2, substitute lineTo command contour.push(prev_x = end_x, prev_y = end_y); break; } //if control is control2 substitute curveTo command GraphicsFactoryHelper.tesselateCurve(prev_x, prev_y, ctrl_x, ctrl_y, prev_x = end_x, prev_y = end_y, contour, false, 0, qualityScale); contour.push(prev_x = end_x, prev_y = end_y); break; } else if (this._minimumCheck(prev_x - ctrl_x, prev_y - ctrl_y)) { //if prev point is control, substitute curveTo command GraphicsFactoryHelper.tesselateCurve(prev_x, prev_y, ctrl_x2, ctrl_y2, prev_x = end_x, prev_y = end_y, contour, false, 0, qualityScale); break; } //console.log("CURVE_TO ", i, ctrl_x, ctrl_y, end_x, end_y); GraphicsFactoryHelper.tesselateCubicCurve(prev_x, prev_y, ctrl_x, ctrl_y, ctrl_x2, ctrl_y2, prev_x = end_x, prev_y = end_y, contour, 0, qualityScale); break; } } }; GraphicsPath.prototype.invalidate = function () { this._orientedBoxBoundsDirty = true; this._dirtyID++; }; GraphicsPath.prototype.getBoxBounds = function (matrix3D, cache, target) { if (matrix3D === void 0) { matrix3D = null; } if (cache === void 0) { cache = null; } if (target === void 0) { target = null; } if (matrix3D) return this._internalGetBoxBounds(matrix3D, cache, target); if (this._orientedBoxBoundsDirty) { this._orientedBoxBoundsDirty = false; this._orientedBoxBounds = this._internalGetBoxBounds(null, this._orientedBoxBounds, null); } if (this._orientedBoxBounds != null) target = this._orientedBoxBounds.union(target, target || cache); return target; }; GraphicsPath.prototype._internalGetBoxBounds = function (matrix3D, cache, target) { if (matrix3D === void 0) { matrix3D = null; } if (cache === void 0) { cache = null; } if (target === void 0) { target = null; } var minX = 0, minY = 0; var maxX = 0, maxY = 0; var len = this._positions.length; if (len == 0) return target; var i = 0; var index = 0; var positions = this._positions[index++]; var pLen = positions.length; var pos1, pos2, rawData; if (matrix3D) rawData = matrix3D._rawData; if (target == null) { target = cache || new Box(); if (matrix3D) { pos1 = positions[i] * rawData[0] + positions[i + 1] * rawData[4] + rawData[12]; pos2 = positions[i] * rawData[1] + positions[i + 1] * rawData[5] + rawData[13]; } else { pos1 = positions[i]; pos2 = positions[i + 1]; } maxX = minX = pos1; maxY = minY = pos2; i += 2; } else { maxX = (minX = target.x) + target.width; maxY = (minY = target.y) + target.height; } for (; i < pLen; i += 2) { if (matrix3D) { pos1 = positions[i] * rawData[0] + positions[i + 1] * rawData[4] + rawData[12]; pos2 = positions[i] * rawData[1] + positions[i + 1] * rawData[5] + rawData[13]; } else { pos1 = positions[i]; pos2 = positions[i + 1]; } if (pos1 < minX) minX = pos1; else if (pos1 > maxX) maxX = pos1; if (pos2 < minY) minY = pos2; else if (pos2 > maxY) maxY = pos2; if (i >= pLen - 2 && index < len) { i = 0; positions = this._positions[index++]; pLen = positions.length; } } target.width = maxX - (target.x = minX); target.height = maxY - (target.y = minY); target.depth = 0; return target; }; GraphicsPath.prototype._minimumCheck = function (lenx, leny) { return (lenx * lenx + leny * leny) < GraphicsPath.lensq; }; GraphicsPath.data_type = '[graphicsdata path]'; GraphicsPath.lensq = Settings.MINIMUM_DRAWING_DISTANCE * Settings.MINIMUM_DRAWING_DISTANCE; return GraphicsPath; }()); export { GraphicsPath };