@awayjs/graphics
Version:
AwayJS graphics classes
281 lines (280 loc) • 11.7 kB
JavaScript
var _a;
import { ImageSampler, AttributesBuffer, Float2Attributes } from '@awayjs/stage';
import { MappingMode, Style, TriangleElements } from '@awayjs/renderer';
import { Shape } from '../renderables/Shape';
import { GradientFillStyle } from './fills/GradientFillStyle';
import { BitmapFillStyle } from './fills/BitmapFillStyle';
import { SolidFillStyle } from './fills/SolidFillStyle';
import { GradientType } from './GradientType';
import { GraphicsFactoryHelper } from './GraphicsFactoryHelper';
import { MaterialManager } from '../managers/MaterialManager';
import { Tess2Provider, TessAsyncService } from '../utils/TessAsyncService';
import { TextureAtlas } from '../managers/TextureAtlas';
/**
* The Graphics class contains a set of methods that you can use to create a
* vector shape. Display objects that support drawing include Sprite and Shape
* objects. Each of these classes includes a <code>graphics</code> property
* that is a Graphics object. The following are among those helper functions
* provided for ease of use: <code>drawRect()</code>,
* <code>drawRoundRect()</code>, <code>drawCircle()</code>, and
* <code>drawEllipse()</code>.
*
* <p>You cannot create a Graphics object directly from ActionScript code. If
* you call <code>new Graphics()</code>, an exception is thrown.</p>
*
* <p>The Graphics class is final; it cannot be subclassed.</p>
*/
//@ts-ignore
var SHAPE_INFO = window.SHAPE_INFO = {
total_time: 0,
tess_time: 0,
multy_contours: 0,
single_contours: 0,
};
var FIXED_BASE = 1000;
export var UnpackFillStyle = (_a = {},
_a[GradientFillStyle.data_type] = function (style, data) {
var material = MaterialManager.getMaterialForGradient(style);
data.material = material;
data.style.image = TextureAtlas.getTextureForGradient(style);
data.style.uvMatrix = style.getUVMatrix();
if (style.type == GradientType.LINEAR) {
material.getTextureAt(0).mappingMode = MappingMode.LINEAR;
}
else if (style.type == GradientType.RADIAL) {
var sampler = data.style.sampler = new ImageSampler();
sampler.imageRect = style.uvRectangle;
material.imageRect = true;
material.getTextureAt(0).mappingMode = MappingMode.RADIAL;
}
return data;
},
// handle solid, we store inside GraphicsFactoryFill
_a[SolidFillStyle.data_type] = function (style, data) {
var material = MaterialManager.getMaterialForColor(style);
data.material = material;
if (material.getNumTextures()) {
data.style.image = TextureAtlas.getTextureForColor(style);
data.style.uvMatrix = style.getUVMatrix();
}
else {
data.style = null;
}
return data;
},
_a[BitmapFillStyle.data_type] = function (style, data) {
data.material = MaterialManager.getMaterialForBitmap(true);
data.style.sampler = new ImageSampler(style.repeat, style.smooth, style.smooth);
data.style.image = style.image;
data.style.uvMatrix = style.getUVMatrix();
return data;
},
_a);
var GraphicsFactoryFills = /** @class */ (function () {
function GraphicsFactoryFills() {
}
Object.defineProperty(GraphicsFactoryFills, "Tess2Wasm", {
get: function () {
return TessAsyncService.instance.module;
},
enumerable: false,
configurable: true
});
GraphicsFactoryFills.prepareWasm = function () {
if (TessAsyncService.instance.status === 'done') {
return;
}
TessAsyncService.instance.init();
};
GraphicsFactoryFills.toFixed = function (val) {
return (val * FIXED_BASE | 0) / FIXED_BASE;
};
GraphicsFactoryFills.nearest = function (x0, y0, x1, y1) {
var dx = (x0 - x1);
(dx < 0) && (dx = -dx);
var dy = (y0 - y1);
(dy < 0) && (dy = -dy);
return (dx + dy) < this.EPS;
};
GraphicsFactoryFills.draw_pathes = function (targetGraphics, clear) {
if (clear === void 0) { clear = false; }
//return;
var pathes = targetGraphics.queued_fill_pathes;
var len = pathes.length;
var shape;
for (var cp = 0; cp < len; cp++) {
var path = pathes[cp];
var pathStyle = path.style;
// there are a bug with shapes
shape = targetGraphics.popEmptyFillShape();
var elements = shape ? shape.elements : null;
var target = elements ? elements.concatenatedBuffer : null;
var newBuffer = this.pathToAttributesBuffer(path, false, target);
if (!newBuffer || !newBuffer.length) {
continue;
}
if (!elements) {
elements = new TriangleElements();
elements.setPositions(new Float2Attributes(newBuffer));
}
else {
elements.invalidate();
elements._numVertices = newBuffer.count;
}
elements.isDynamic = targetGraphics._clearCount > 0;
var data = {
style: new Style(),
material: null
};
if (!(pathStyle.fillStyle.data_type in UnpackFillStyle)) {
console.error('Unknown style:', pathStyle.fillStyle.data_type);
}
UnpackFillStyle[pathStyle.fillStyle.data_type](pathStyle.fillStyle, data);
shape = shape || Shape.getShape(elements);
shape.style = data.style;
shape.material = data.material;
shape.originalFillStyle = pathStyle.fillStyle;
targetGraphics.addShapeInternal(shape);
}
targetGraphics.queued_fill_pathes.length = 0;
if (clear) {
targetGraphics._active_fill_path = null;
targetGraphics._lastFill = null;
}
else if (shape) {
targetGraphics.queued_fill_pathes.push(targetGraphics._active_fill_path);
targetGraphics._lastFill = shape;
}
};
GraphicsFactoryFills.prepareContours = function (graphicsPath, applyFix, qualityScale) {
if (applyFix === void 0) { applyFix = false; }
if (qualityScale === void 0) { qualityScale = 1; }
graphicsPath.prepare(qualityScale);
var contours = graphicsPath._positions;
var finalContours = [];
for (var k = 0; k < contours.length; k++) {
var contour = contours[k];
// same as map, but without allocation
var closed_1 = this.nearest(contour[0], contour[1], contour[contour.length - 2], contour[contour.length - 1]);
// make sure start and end point of a contour are not the same
if (closed_1) {
contour.pop();
contour.pop();
}
// all contours should already be prepared by GraphicsPath.prepare()
// we only want to make sure that each contour contains at least 3 pairs of x/y positions
// otherwise there is no way they can form a shape
if (contour.length >= 6) {
if (applyFix) {
// tess2 fix
// there are problems with small shapes
// encrease a size
var fixed = new Array(contour.length);
for (var i = 0, l = contour.length; i < l; i++) {
fixed[i] = this.toFixed(contour[i] * this.TESS_SCALE);
}
finalContours.push(fixed);
}
else {
finalContours.push(contour);
}
}
}
return finalContours;
};
GraphicsFactoryFills.runTesselator = function (graphicsPath, qualityScale) {
if (qualityScale === void 0) { qualityScale = 1; }
var finalContours = this.prepareContours(graphicsPath, this.USE_TESS_FIX, qualityScale);
if (finalContours.length > 0) {
SHAPE_INFO.multy_contours += 1;
}
else {
SHAPE_INFO.single_contours += 1;
}
if (finalContours.length === 0) {
return null;
}
return Tess2Provider.tesselate({ contours: finalContours,
windingRule: 0,
elementType: 0,
polySize: 3,
vertexSize: 2
});
};
GraphicsFactoryFills.fillBuffer = function (result, finalVerts) {
var numElems = result.elements.length;
var scale = this.USE_TESS_FIX ? (1 / this.TESS_SCALE) : 1;
var vindex = 0;
var p1x = 0;
var p1y = 0;
var p2x = 0;
var p2y = 0;
var p3x = 0;
var p3y = 0;
for (var i = 0; i < numElems; i += 3) {
p1x = scale * result.vertices[result.elements[i + 0] * 2 + 0];
p1y = scale * result.vertices[result.elements[i + 0] * 2 + 1];
p2x = scale * result.vertices[result.elements[i + 1] * 2 + 0];
p2y = scale * result.vertices[result.elements[i + 1] * 2 + 1];
p3x = scale * result.vertices[result.elements[i + 2] * 2 + 0];
p3y = scale * result.vertices[result.elements[i + 2] * 2 + 1];
if (GraphicsFactoryHelper.isClockWiseXY(p1x, p1y, p2x, p2y, p3x, p3y)) {
finalVerts[vindex++] = p3x;
finalVerts[vindex++] = p3y;
finalVerts[vindex++] = p2x;
finalVerts[vindex++] = p2y;
finalVerts[vindex++] = p1x;
finalVerts[vindex++] = p1y;
}
else {
finalVerts[vindex++] = p1x;
finalVerts[vindex++] = p1y;
finalVerts[vindex++] = p2x;
finalVerts[vindex++] = p2y;
finalVerts[vindex++] = p3x;
finalVerts[vindex++] = p3y;
}
}
return finalVerts;
};
GraphicsFactoryFills.pathToAttributesBuffer = function (graphicsPath, closePath, target, qualityScale) {
if (closePath === void 0) { closePath = true; }
if (target === void 0) { target = null; }
if (qualityScale === void 0) { qualityScale = 1; }
var start = performance.now();
var resultVertexSize = graphicsPath.verts
? graphicsPath.verts.length
: 0;
var tesselatedVertexSize = 0;
var res = this.runTesselator(graphicsPath, qualityScale);
if (res && res.elements.length > 0) {
tesselatedVertexSize = res.elements.length * 2;
resultVertexSize += res.elements.length * 2;
}
var vertexSize = 2;
if (!target) {
target = new AttributesBuffer(Float32Array.BYTES_PER_ELEMENT * vertexSize, (resultVertexSize / vertexSize) | 0);
}
// resize is safe, it not rebuild buffer when count is same.
// count - count of 2 dimension vertex, divide on 2
target.count = (resultVertexSize / vertexSize) | 0;
// fill direct to Float32Array
var finalVerts = new Float32Array(target.buffer);
if (res) {
this.fillBuffer(res, finalVerts);
Tess2Provider.dispose();
}
// merge poly vertex
var vs = graphicsPath.verts.length;
for (var i = 0; i < vs; i++) {
finalVerts[tesselatedVertexSize + i] = graphicsPath.verts[i];
}
SHAPE_INFO.total_time += performance.now() - start;
return target;
};
GraphicsFactoryFills.TESS_SCALE = 20;
GraphicsFactoryFills.USE_TESS_FIX = true;
GraphicsFactoryFills.EPS = 1.0 / FIXED_BASE;
return GraphicsFactoryFills;
}());
export { GraphicsFactoryFills };