UNPKG

@awayjs/graphics

Version:
228 lines (227 loc) 9.32 kB
import { assert, clamp } from './utilities'; import { GradientType } from '../draw/GradientType'; import { FillType } from './FillType'; import { GraphicsFillStyle } from '../draw/GraphicsFillStyle'; import { Matrix } from '@awayjs/core'; import { GradientFillStyle } from '../draw/fills/GradientFillStyle'; import { BitmapFillStyle } from '../draw/fills/BitmapFillStyle'; import { SolidFillStyle } from '../draw/fills/SolidFillStyle'; import { LineScaleMode } from '@awayjs/renderer'; import { CapsStyle } from '../draw/CapsStyle'; import { GraphicsStrokeStyle } from '../draw/GraphicsStrokeStyle'; var SegmentedPath = /** @class */ (function () { function SegmentedPath(fillStyle, lineStyle) { this.fillStyle = fillStyle; this.lineStyle = lineStyle; this._head = null; } SegmentedPath.prototype.addSegment = function (segment1) { assert(segment1); assert(segment1.next === null); assert(segment1.prev === null); var currentHead = this._head; if (currentHead) { assert(segment1 !== currentHead); currentHead.next = segment1; segment1.prev = currentHead; } this._head = segment1; }; // Does *not* reset the segment1's prev and next pointers! SegmentedPath.prototype.removeSegment = function (segment1) { if (segment1.prev) { segment1.prev.next = segment1.next; } if (segment1.next) { segment1.next.prev = segment1.prev; } }; SegmentedPath.prototype.insertSegment = function (segment1, next) { var prev = next.prev; segment1.prev = prev; segment1.next = next; if (prev) { prev.next = segment1; } next.prev = segment1; }; SegmentedPath.prototype.head = function () { return this._head; }; SegmentedPath.prototype.rgbaToArgb = function (float32Color) { var r = (float32Color & 0xff000000) >>> 24; var g = (float32Color & 0xff0000) >>> 16; var b = (float32Color & 0xff00) >>> 8; var a = float32Color & 0xff; return (a << 24) | (r << 16) | (g << 8) | b; }; SegmentedPath.prototype.getAlpha = function (float32Color) { //var r:number = ( float32Color & 0xff000000 ) >>> 24; //var g:number = ( float32Color & 0xff0000 ) >>> 16; //var b:number = ( float32Color & 0xff00 ) >>> 8; var a = float32Color & 0xff; return a; }; SegmentedPath.prototype.rgbToArgb = function (float32Color) { var a = (float32Color & 0xff000000) >>> 24; var b = (float32Color & 0xff0000) >>> 16; var g = (float32Color & 0xff00) >>> 8; var r = float32Color & 0xff; return (a << 24) | (r << 16) | (g << 8) | b; }; SegmentedPath.prototype.applyStyle = function (shape, style, isFill) { if (isFill === void 0) { isFill = true; } var awayStyle; var fillStyle; switch (style.type) { case FillType.Solid: { style.alpha = (style.color & 0xff) / 0xff; style.color = this.rgbaToArgb(style.color); fillStyle = new SolidFillStyle(style.color, style.alpha); break; } case FillType.LinearGradient: case FillType.RadialGradient: case FillType.FocalRadialGradient: { var gradientType = style.type === FillType.LinearGradient ? GradientType.LINEAR : GradientType.RADIAL; var alphas = []; for (var i = 0; i < style.colors.length; i++) { alphas[i] = (style.colors[i] & 0xff) / 0xff; style.colors[i] = this.rgbaToArgb(style.colors[i]); } var awayMatrix = new Matrix(style.transform.a, style.transform.b, style.transform.c, style.transform.d, style.transform.tx, style.transform.ty); fillStyle = new GradientFillStyle(gradientType, style.colors, alphas, style.ratios, awayMatrix, style.spreadMethod, style.interpolationMode, style.focalPoint / 2 | 0); break; } case FillType.ClippedBitmap: case FillType.RepeatingBitmap: case FillType.NonsmoothedClippedBitmap: case FillType.NonsmoothedRepeatingBitmap: { var awayMatrix = new Matrix(style.transform.a, style.transform.b, style.transform.c, style.transform.d, style.transform.tx, style.transform.ty); fillStyle = new BitmapFillStyle(style.material, awayMatrix, style.repeat, style.smooth); break; } default: { console.error('Unknown style type:', style.type); } } if (isFill) { awayStyle = new GraphicsFillStyle(fillStyle); } else { // TODO: Figure out how to handle startCapsStyle var thickness = (clamp(style.width, 0, 0xff * 20) | 0) / 20; var scaleModeAWJ = thickness <= 0.05 ? LineScaleMode.HAIRLINE : LineScaleMode.NORMAL; if (style.startCapsStyle != style.endCapsStyle) { console.log('Warning: different end vs start capstyle'); } style.startCapsStyle = CapsStyle.ROUND; style.jointStyle = 0; awayStyle = new GraphicsStrokeStyle(fillStyle, thickness, style.jointStyle, style.startCapsStyle, style.miterLimit, scaleModeAWJ); } shape.style = awayStyle; }; SegmentedPath.prototype.serializeAJS = function (shape, morphShape) { //console.log("serializeAJS"); var segment1 = this.head(); if (!segment1) { // Path is empty. return; } var isFill = !!this.fillStyle; var mainStyle = isFill ? this.fillStyle : this.lineStyle; var morphStyle = mainStyle.morph; this.applyStyle(shape, mainStyle, isFill); if (morphStyle && morphShape) { this.applyStyle(morphShape, morphStyle, isFill); } while (segment1) { segment1.storeStartAndEnd(); segment1 = segment1.prev; } var start = this.head(); var end = start; var finalRoot = null; var finalHead = null; // Path segments for one style can appear in arbitrary order in the tag's list // of edge records. // Before we linearize them, we have to identify all pairs of segments where // one ends at a coordinate the other starts at. // The following loop does that, by creating ever-growing runs of matching // segments. If no more segments are found that match the current run (either // at the beginning, or at the end), the current run is complete, and a new // one is started. Rinse, repeat, until no solitary segments remain. var current = start.prev; while (start) { while (current) { // if this segment1 has the same startpoint as the start-startpoint it needs to be reversed. if (current.startConnectsTo(start)) { current.flipDirection(); } // if this segment1 connects to another, we remove it and add it at the end. if (current.connectsTo(start)) { if (current.next !== start) { this.removeSegment(current); this.insertSegment(current, start); } start = current; current = start.prev; continue; } if (current.startConnectsTo(end)) { current.flipDirection(); } if (end.connectsTo(current)) { this.removeSegment(current); end.next = current; current = current.prev; end.next.prev = end; end.next.next = null; end = end.next; continue; } current = current.prev; } // This run of segments is finished. Store and forget it (for this loop). current = start.prev; if (!finalRoot) { finalRoot = start; finalHead = end; } else { finalHead.next = start; start.prev = finalHead; finalHead = end; finalHead.next = null; } if (!current) { break; } start = end = current; current = start.prev; } var lastPosition = { x: 0, y: 0 }; current = finalRoot; while (current) { if (current.isValidFill) { current.serializeAJS(shape, morphShape, lastPosition); } current = current.next; } /* if (this.fillStyle) { shape.endFill(); } else { shape.endLine(); }*/ return shape; }; return SegmentedPath; }()); export { SegmentedPath };