@awayjs/graphics
Version:
AwayJS graphics classes
342 lines (340 loc) • 14.8 kB
JavaScript
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 };