UNPKG

@awayjs/graphics

Version:
301 lines (300 loc) 12.8 kB
var _a; import { Matrix } from '@awayjs/core'; 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'; /** * 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 obj = MaterialManager.getMaterialForGradient(style); var material = obj.material; data.material = obj.material; data.material.animateUVs = true; data.style.addSamplerAt(data.sampler, material.getTextureAt(0)); data.style.uvMatrix = style.getUVMatrix(); if (style.type == GradientType.LINEAR) { material.getTextureAt(0).mappingMode = MappingMode.LINEAR; } else if (style.type == GradientType.RADIAL) { data.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 obj = MaterialManager.getMaterialForColor(style.color, style.alpha); var material = obj.material; data.material = material; if (obj.colorPos) { material.animateUVs = true; data.style.addSamplerAt(data.sampler, material.getTextureAt(0)); data.style.uvMatrix = new Matrix(0, 0, 0, 0, obj.colorPos.x, obj.colorPos.y); style.uvMatrix = data.style.uvMatrix; } else { data.style = data.sampler = null; } return data; }, _a[BitmapFillStyle.data_type] = function (style, data) { //new ITexture(ImageUtils.getDefaultImage2D());//bitmapStyle.texture; var material = style.material; data.material = material; data.sampler.repeat = style.repeat; data.sampler.smooth = style.smooth; data.sampler.mipmap = style.smooth; material.style.sampler = data.sampler; material.animateUVs = true; data.style.addSamplerAt(data.sampler, material.getTextureAt(0)); 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) { //return; var pathes = targetGraphics.queued_fill_pathes; var len = pathes.length; for (var cp = 0; cp < len; cp++) { var path = pathes[cp]; var pathStyle = path.style; // there are a bug with shapes var 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 = { sampler: new ImageSampler(), 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; }; 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); /* workaround for wasm crash if (finalContours.length > 0) { const firstContour = finalContours[0]; if (firstContour.length >= 6) { if (firstContour[0] == 0 && firstContour[1] == 0 && firstContour[2] == 0 && firstContour[3] == 0 && firstContour[4] == 0 && firstContour[5] == 0) { finalContours = this.prepareContours(graphicsPath, false); } else if (firstContour[1] == 0 && firstContour[3] == 0 && firstContour[5] == 0) { finalContours = this.prepareContours(graphicsPath, false); } else if (firstContour[0] == 0 && firstContour[2] == 0 && firstContour[4] == 0) { finalContours = this.prepareContours(graphicsPath, false); } } }*/ 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 };