import { ArgumentError, RangeError, Point, Matrix, Matrix3D, AssetBase, Rectangle, WeakAssetSet, IAsset, } from '@awayjs/core'; import { BitmapImage2D, Image2D, ImageSampler } from '@awayjs/stage'; import { IContainer, IEntityTraverser, PickEntity } from '@awayjs/view'; import { IMaterial, Style, TriangleElements, LineElements, LineScaleMode, IMaterialFactory } from '@awayjs/renderer'; import { GraphicsPath } from './draw/GraphicsPath'; import { GraphicsFactoryFills } from './draw/GraphicsFactoryFills'; import { GraphicsFactoryStrokes } from './draw/GraphicsFactoryStrokes'; import { GraphicsFactoryHelper } from './draw/GraphicsFactoryHelper'; import { InterpolationMethod } from './draw/InterpolationMethod'; import { JointStyle } from './draw/JointStyle'; import { TriangleCulling } from './draw/TriangleCulling'; import { SpreadMethod } from './draw/SpreadMethod'; import { CapsStyle } from './draw/CapsStyle'; import { GradientType } from './draw/GradientType'; import { BitmapFillStyle } from './draw/fills/BitmapFillStyle'; import { GradientFillStyle } from './draw/fills/GradientFillStyle'; import { SolidFillStyle } from './draw/fills/SolidFillStyle'; import { GraphicsPathWinding } from './draw/GraphicsPathWinding'; import { IGraphicsData } from './draw/IGraphicsData'; import { GraphicsStrokeStyle } from './draw/GraphicsStrokeStyle'; import { GraphicsFillStyle } from './draw/GraphicsFillStyle'; import { Shape } from './renderables/Shape'; import { SegmentedPath } from './data/SegmentedPath'; import { FillType } from './data/FillType'; import { PathSegment } from './data/PathSegment'; import { assert } from './data/utilities'; import { MaterialManager } from './managers/MaterialManager'; import { ManagedPool } from './ManagedPool'; import { Settings } from './Settings'; import { FillStyle, LineStyle, ShapeStyle } from './flash/ShapeStyle'; import { BBox, ShapeRecord, ShapeRecordFlags, ShapeTag } from './flash/ShapeTag'; import { StyleUtils } from './flash/StyleUtils'; import { GraphicsPathCommand } from './draw/GraphicsPathCommand'; GraphicsFactoryFills.prepareWasm(); const Array_push = Array.prototype.push; const fromTwips = (val: number) => Math.round(val / 20); /** * * Graphics is a collection of Shapes, each of which contain the actual geometrical data such as vertices, * normals, uvs, etc. It also contains a reference to an animation class, which defines how the geometry moves. * A Graphics object is assigned to a Sprite, a scene graph occurence of the geometry, which in turn assigns * the SubGeometries to its respective TriangleGraphic objects. * * * * * @class Graphics */ export class Graphics extends AssetBase { private static _pool: Array = new Array(); public static getShapeForBitmapStyle (shapeStyle: ShapeStyle, flashBox: BBox): Shape { const style = new Style(); const rect = new Rectangle( fromTwips(flashBox.xMin), fromTwips(flashBox.yMin), fromTwips(flashBox.xMax - flashBox.xMin), fromTwips(flashBox.yMax - flashBox.yMin), ); const element = Shape.getTriangleElement(rect); element.usages++; const { a, b, c, d, tx, ty } = shapeStyle.transform; style.image = shapeStyle.image; const bitmapFillStyle = new BitmapFillStyle( shapeStyle.image, new Matrix(a, b, c ,d, tx, ty), shapeStyle.repeat, shapeStyle.smooth ); const material = MaterialManager.getMaterialForBitmap(true); //enforce image smooth style style.sampler = new ImageSampler(shapeStyle.repeat, shapeStyle.smooth, shapeStyle.smooth); style.uvMatrix = bitmapFillStyle.getUVMatrix(); return Shape.getShape(element, material, style); } public static getGraphics(): Graphics { return (Graphics._pool.length) ? Graphics._pool.pop() : new Graphics(); } public static clearPool() { Graphics._pool = []; } public static assetType: string = '[asset Graphics]'; private _bitmapFillPool: NumberMap> = {}; private _queuedShapeTags: ShapeTag[] = []; private _shapes: Array = []; private _queued_fill_pathes: GraphicsPath[] = []; private _queued_stroke_pathes: GraphicsPath[] = []; public _active_fill_path: GraphicsPath; public _active_stroke_path: GraphicsPath; private _lineStyle: GraphicsStrokeStyle; private _fillStyle: GraphicsFillStyle; private _current_position: Point = new Point(); public tryOptimiseSigleImage: boolean = false; public _lastFill: Shape; public _lastStroke: Shape; private _drawingDirty: boolean = false; private _owners: WeakAssetSet = new WeakAssetSet(); public _start: GraphicsPath[]; public _end: GraphicsPath[]; /*private*/ _clearCount: number = 0; private _internalShapesId: number[] = []; private _rFillPool: ManagedPool = new ManagedPool(Shape, 100, false); private _rStrokePool: ManagedPool = new ManagedPool(Shape, 100, false); private _poolingConfig = { fill: Settings.ALLOW_INTERNAL_POOL.FILLS, stroke: Settings.ALLOW_INTERNAL_POOL.STROKES, clearsCount: Settings.CLEARS_BEFORE_POOLING } // graphics, from it was copied public sourceGraphics: Graphics; get start(): GraphicsPath[] { if (!this._start && this.sourceGraphics) { return this.sourceGraphics.start; } return this._start; } get end(): GraphicsPath[] { if (!this._end && this.sourceGraphics) { return this.sourceGraphics.end; } return this._end; } set start(v: GraphicsPath[]) { this._start = v; } set end(v: GraphicsPath[]) { this._end = v; } public get assetType(): string { return Graphics.assetType; } public get count(): number { return ( this._shapes.length + this._queued_stroke_pathes.length + this._queued_fill_pathes.length + this._queuedShapeTags.length); } public get queued_stroke_pathes(): Array { return this._queued_stroke_pathes; } public set queued_stroke_pathes(value: Array) { this._queued_stroke_pathes = value; } public get queued_fill_pathes(): Array { return this._queued_fill_pathes; } public set queued_fill_pathes(value: Array) { this._queued_fill_pathes = value; } public add_queued_path(value: GraphicsPath, supressFill = false) { if (!value.style) { return; } const isLine = value.style.data_type === GraphicsStrokeStyle.data_type; if (!isLine) { this._drawingDirty = true; this._queued_fill_pathes.push(value); } else { this._queued_stroke_pathes.push(value); if (!supressFill) { this.endFill(); } } } /** * Creates a new Graphics object. */ constructor() { super(); } /* internal */ set internalPoolConfig (v: {stroke: boolean, fill: boolean} | boolean) { this._poolingConfig.fill = typeof v === 'boolean' ? v : v.fill; this._poolingConfig.stroke = typeof v === 'boolean' ? v : v.stroke; if (!v) { this._rFillPool.enabled && this._rFillPool.clear(); this._rStrokePool.enabled && this._rStrokePool.clear(); this._rStrokePool.enabled = false; this._rStrokePool.enabled = false; } this._clearCount = 0; } get internalPoolConfig () { return this._poolingConfig; } public popEmptyFillShape() { return this._rFillPool.pop(); } public popEmptyStrokeShape() { return this._rStrokePool.pop(); } /* internal */ addShapeInternal(shape: Shape) { this.addShape(shape); this._internalShapesId.push(shape.id); } public addOwner(owner: IContainer): void { this._owners.add(owner); } public removeOwner(owner: IContainer): void { this._owners.remove(owner); } public invalidate(): void { super.invalidate(); this._owners.forEach((asset: IContainer) => asset.invalidate()); } /** * Adds a GraphicBase wrapping a Elements. * * @param elements */ public addShape(shape: Shape): Shape { shape.usages++; const shapeIndex: number = this.getShapeIndex(shape); if (shapeIndex != -1) this.removeShapeAt(shapeIndex); this._shapes.push(shape); this.invalidate(); return shape; } public removeShape(shape: Shape): void { const shapeIndex: number = this.getShapeIndex(shape); if (shapeIndex == -1) throw new ArgumentError('Shape parameter is not a shape of the caller'); this.removeShapeAt(shapeIndex); } public removeShapeAt(index: number): void { if (index < 0 || index >= this._shapes.length) throw new RangeError('Index is out of range'); const shape: Shape = this._shapes.splice(index, 1)[0]; shape.usages--; if (!shape.usages) { if (!this.tryPoolShape(shape)) { shape.dispose(); } } this.invalidate(); } public getShapeAt(index: number): Shape { return this._shapes[index]; } public getShapeIndex(shape: Shape): number { return this._shapes.indexOf(shape); } public applyTransformation(transform: Matrix3D): void { const len: number = this._shapes.length; for (let i: number = 0; i < len; ++i) { this._shapes[i].applyTransformation(transform); } } public copyTo(graphics: Graphics, cloneShapes: boolean = false): void { if (this._drawingDirty) this.endFill(); graphics.sourceGraphics = this; graphics._addShapes(this._shapes, cloneShapes); } public clone(cloneShapes: boolean = false): Graphics { const newInstance: Graphics = Graphics.getGraphics(); this.copyTo(newInstance, cloneShapes); return newInstance; } /** * Scales the geometry. * @param scale The amount by which to scale. */ public scale(scale: number): void { const len: number = this._shapes.length; for (let i: number = 0; i < len; ++i) this._shapes[i].scale(scale); } private tryPoolShape(shape: Shape): boolean { // not works atm // text is bugged const canPooledTriangle = shape.elements.assetType === TriangleElements.assetType; const canPooledLine = shape.elements.assetType === LineElements.assetType; if (!canPooledLine && !canPooledTriangle) { return false; } const index = this._internalShapesId.indexOf(shape.id); if (index === -1) { return false; } if (canPooledTriangle) { return this._rFillPool.store(shape); } if (canPooledLine) { return this._rStrokePool.store(shape); } return false; } public clear(): void { this._clearCount++; this._lastFill = null; this._lastStroke = null; const requireShapePool = ( this._internalShapesId.length > 0 && this._clearCount >= this._poolingConfig.clearsCount); if (requireShapePool && ( this._rStrokePool.enabled !== this._poolingConfig.stroke || this._rFillPool.enabled !== this._poolingConfig.fill )) { console.warn( '[Graphics] To many clears, pooling shapes internally!', this.id, this._internalShapesId.length); this._rFillPool.enabled = this._poolingConfig.fill; this._rStrokePool.enabled = this._poolingConfig.stroke; } let shape: Shape; const len: number = this._shapes.length; for (let i: number = 0; i < len; i++) { shape = this._shapes[i]; shape.usages--; if (!shape.usages) { if (!this.tryPoolShape(shape)) { shape.dispose(); } } } this._internalShapesId.length = 0; this._shapes.length = 0; this.invalidate(); this._active_fill_path = null; this._active_stroke_path = null; this._queued_fill_pathes.length = 0; this._queued_stroke_pathes.length = 0; this._current_position.x = 0; this._current_position.y = 0; this._drawingDirty = false; this._lineStyle = null; this._fillStyle = null; } /** * Clears all resources used by the Graphics object, including SubGeometries. */ public dispose(): void { this.clear(); this._bitmapFillPool = null; /* we can not release shapes for this, it was a store elements that can be reused then */ this._rFillPool.clear(); this._rStrokePool.clear(); this._internalShapesId.length = 0; this._clearCount = 0; Graphics._pool.push(this); } /** * Scales the uv coordinates (tiling) * @param scaleU The amount by which to scale on the u axis. Default is 1; * @param scaleV The amount by which to scale on the v axis. Default is 1; */ public scaleUV(scaleU: number = 1, scaleV: number = 1): void { const len: number = this._shapes.length; for (let i: number = 0; i < len; ++i) this._shapes[i].scaleUV(scaleU, scaleV); } // public invalidateMaterials():void // { // var len:number = this._shapes.length; // for (var i:number = 0; i < len; ++i) // this._shapes[i].invalidateMaterial(); // } // public invalidateElements():void // { // var len:number = this._shapes.length; // for (var i:number = 0; i < len; ++i) // this._shapes[i].invalidateElements(); // } public _acceptTraverser(traverser: IEntityTraverser): void { // this is important // not close shape when request bounds // otherwise it will corrupt rendering flow if (this._drawingDirty) { // need to drop shape that was pre-built but not a closed (_endFillInternal(false)) // because shape was corrupted when a bounds calculation requested between commands if (this._lastFill) this.removeShape(this._lastFill); if (this._lastStroke) this.removeShape(this._lastStroke); if (traverser instanceof PickEntity) { // build shape construct shapes but not close graphics this._endFillInternal(false); } else { this.endFill(); } } const len = this._shapes.length; for (let i: number = 0; i < len; i++) traverser.applyTraversable(this._shapes[i]); } /** * Fills a drawing area with a bitmap image. The bitmap can be repeated or * tiled to fill the area. The fill remains in effect until you call the * beginFill(), beginBitmapFill(), * beginGradientFill(), or beginShaderFill() * method. Calling the clear() method clears the fill. * *

The application renders the fill whenever three or more points are * drawn, or when the endFill() method is called.

* * @param bitmap A transparent or opaque bitmap image that contains the bits * to be displayed. * @param matrix A matrix object(of the flash.geom.Matrix class), which you * can use to define transformations on the bitmap. For * example, you can use the following matrix to rotate a bitmap * by 45 degrees(pi/4 radians): * @param repeat If true, the bitmap image repeats in a tiled * pattern. If false, the bitmap image does not * repeat, and the edges of the bitmap are used for any fill * area that extends beyond the bitmap. * *

For example, consider the following bitmap(a 20 x * 20-pixel checkerboard pattern):

* *

When repeat is set to true(as * in the following example), the bitmap fill repeats the * bitmap:

* *

When repeat is set to false, * the bitmap fill uses the edge pixels for the fill area * outside the bitmap:

* @param smooth If false, upscaled bitmap images are rendered * by using a nearest-neighbor algorithm and look pixelated. If * true, upscaled bitmap images are rendered by * using a bilinear algorithm. Rendering by using the nearest * neighbor algorithm is faster. */ public beginBitmapFill( bitmap: BitmapImage2D, matrix: Matrix = null, repeat: boolean = true, smooth: boolean = Settings.SMOOTH_BITMAP_FILL_DEFAULT ): void { if (this._fillStyle) this.endFill(); if (!this._bitmapFillPool) { this._bitmapFillPool = {}; } let fill = this._bitmapFillPool[bitmap.id]; if (!fill) { fill = this._bitmapFillPool[bitmap.id] = new GraphicsFillStyle( new BitmapFillStyle( bitmap, matrix, repeat, smooth) ); } else { fill.fillStyle.matrix = matrix; fill.fillStyle.repeat = repeat; fill.fillStyle.smooth = smooth; } this._fillStyle = fill; this._updateFillPath(); } /** * Specifies a simple one-color fill that subsequent calls to other Graphics * methods(such as lineTo() or drawCircle()) use * when drawing. The fill remains in effect until you call the * beginFill(), beginBitmapFill(), * beginGradientFill(), or beginShaderFill() * method. Calling the clear() method clears the fill. * *

The application renders the fill whenever three or more points are * drawn, or when the endFill() method is called.

* * @param color The color of the fill(0xRRGGBB). * @param alpha The alpha value of the fill(0.0 to 1.0). */ public beginFill(color: number /*int*/, alpha: number = 1): void { if (color == 0) color = 0x010101; if (this._fillStyle) this.endFill(); this._fillStyle = new GraphicsFillStyle(new SolidFillStyle(color, alpha)); this._updateFillPath(); } /** * Specifies a gradient fill used by subsequent calls to other Graphics * methods(such as lineTo() or drawCircle()) for * the object. The fill remains in effect until you call the * beginFill(), beginBitmapFill(), * beginGradientFill(), or beginShaderFill() * method. Calling the clear() method clears the fill. * *

The application renders the fill whenever three or more points are * drawn, or when the endFill() method is called.

* * @param type A value from the GradientType class that * specifies which gradient type to use: * GradientType.LINEAR or * GradientType.RADIAL. * @param colors An array of RGB hexadecimal color values used * in the gradient; for example, red is 0xFF0000, * blue is 0x0000FF, and so on. You can specify * up to 15 colors. For each color, specify a * corresponding value in the alphas and ratios * parameters. * @param alphas An array of alpha values for the corresponding * colors in the colors array; valid values are 0 * to 1. If the value is less than 0, the default * is 0. If the value is greater than 1, the * default is 1. * @param ratios An array of color distribution ratios; valid * values are 0-255. This value defines the * percentage of the width where the color is * sampled at 100%. The value 0 represents the * left position in the gradient box, and 255 * represents the right position in the gradient * box. * @param matrix A transformation matrix as defined by the * flash.geom.Matrix class. The flash.geom.Matrix * class includes a * createGradientBox() method, which * lets you conveniently set up the matrix for use * with the beginGradientFill() * method. * @param spreadMethod A value from the SpreadMethod class that * specifies which spread method to use, either: * SpreadMethod.PAD, * SpreadMethod.REFLECT, or * SpreadMethod.REPEAT. * *

For example, consider a simple linear * gradient between two colors:

* *

This example uses * SpreadMethod.PAD for the spread * method, and the gradient fill looks like the * following:

* *

If you use SpreadMethod.REFLECT * for the spread method, the gradient fill looks * like the following:

* *

If you use SpreadMethod.REPEAT * for the spread method, the gradient fill looks * like the following:

* @param interpolationMethod A value from the InterpolationMethod class that * specifies which value to use: * InterpolationMethod.LINEAR_RGB or * InterpolationMethod.RGB * *

For example, consider a simple linear * gradient between two colors(with the * spreadMethod parameter set to * SpreadMethod.REFLECT). The * different interpolation methods affect the * appearance as follows:

* @param focalPointRatio A number that controls the location of the * focal point of the gradient. 0 means that the * focal point is in the center. 1 means that the * focal point is at one border of the gradient * circle. -1 means that the focal point is at the * other border of the gradient circle. A value * less than -1 or greater than 1 is rounded to -1 * or 1. For example, the following example shows * a focalPointRatio set to 0.75: * @throws ArgumentError If the type parameter is not valid. */ public beginGradientFill( type: GradientType, colors: number[], alphas: number[], ratios: number[], matrix: Matrix = null, spreadMethod: string = 'pad', interpolationMethod: string = 'rgb', focalPointRatio: number = 0 ): void { if (this._fillStyle) this.endFill(); this._fillStyle = new GraphicsFillStyle( new GradientFillStyle( type, colors, alphas, ratios, matrix, spreadMethod, interpolationMethod, focalPointRatio ) ); this._updateFillPath(); } /** * Copies all of drawing commands from the source Graphics object into the * calling Graphics object. * * @param sourceGraphics The Graphics object from which to copy the drawing * commands. */ public copyFrom(sourceGraphics: Graphics): void { sourceGraphics.copyTo(this); } /** * Draws a cubic Bezier curve from the current drawing position to the * specified anchor point. Cubic Bezier curves consist of two anchor points * and two control points. The curve interpolates the two anchor points and * curves toward the two control points. * * The four points you use to draw a cubic Bezier curve with the * cubicCurveTo() method are as follows: * *
    *
  • The current drawing position is the first anchor point.
  • *
  • The anchorX and anchorY parameters specify the second anchor point. *
  • *
  • The controlX1 and controlY1 parameters * specify the first control point.
  • *
  • The controlX2 and controlY2 parameters * specify the second control point.
  • *
* * If you call the cubicCurveTo() method before calling the * moveTo() method, your curve starts at position (0, 0). * * If the cubicCurveTo() method succeeds, the Flash runtime sets * the current drawing position to (anchorX, * anchorY). If the cubicCurveTo() method fails, * the current drawing position remains unchanged. * * If your movie clip contains content created with the Flash drawing tools, * the results of calls to the cubicCurveTo() method are drawn * underneath that content. * * @param controlX1 Specifies the horizontal position of the first control * point relative to the registration point of the parent * display object. * @param controlY1 Specifies the vertical position of the first control * point relative to the registration point of the parent * display object. * @param controlX2 Specifies the horizontal position of the second control * point relative to the registration point of the parent * display object. * @param controlY2 Specifies the vertical position of the second control * point relative to the registration point of the parent * display object. * @param anchorX Specifies the horizontal position of the anchor point * relative to the registration point of the parent display * object. * @param anchorY Specifies the vertical position of the anchor point * relative to the registration point of the parent display * object. */ public cubicCurveTo( controlX1: number, controlY1: number, controlX2: number, controlY2: number, anchorX: number, anchorY: number): void { this._drawingDirty = true; if (this._active_fill_path) this._active_fill_path.cubicCurveTo(controlX1, controlY1, controlX2, controlY2, anchorX, anchorY); if (this._active_stroke_path) this._active_stroke_path.cubicCurveTo(controlX1, controlY1, controlX2, controlY2, anchorX, anchorY); this._current_position.x = anchorX; this._current_position.y = anchorY; this.invalidate(); } /** * Draws a curve using the current line style from the current drawing * position to(anchorX, anchorY) and using the control point that * (controlX, controlY) specifies. The current * drawing position is then set to(anchorX, * anchorY). If the movie clip in which you are drawing contains * content created with the Flash drawing tools, calls to the * curveTo() method are drawn underneath this content. If you * call the curveTo() method before any calls to the * moveTo() method, the default of the current drawing position * is(0, 0). If any of the parameters are missing, this method fails and the * current drawing position is not changed. * *

The curve drawn is a quadratic Bezier curve. Quadratic Bezier curves * consist of two anchor points and one control point. The curve interpolates * the two anchor points and curves toward the control point.

* * @param controlX A number that specifies the horizontal position of the * control point relative to the registration point of the * parent display object. * @param controlY A number that specifies the vertical position of the * control point relative to the registration point of the * parent display object. * @param anchorX A number that specifies the horizontal position of the * next anchor point relative to the registration point of * the parent display object. * @param anchorY A number that specifies the vertical position of the next * anchor point relative to the registration point of the * parent display object. */ public curveTo(controlX: number, controlY: number, anchorX: number, anchorY: number): void { this._drawingDirty = true; if (this._active_fill_path) this._active_fill_path.curveTo(controlX, controlY, anchorX, anchorY); if (this._active_stroke_path) this._active_stroke_path.curveTo(controlX, controlY, anchorX, anchorY); this._current_position.x = anchorX; this._current_position.y = anchorY; this.invalidate(); } /** * Draws a circle. Set the line style, fill, or both before you call the * drawCircle() method, by calling the linestyle(), * lineGradientStyle(), beginFill(), * beginGradientFill(), or beginBitmapFill() * method. * * @param x The x location of the center of the circle relative * to the registration point of the parent display object(in * pixels). * @param y The y location of the center of the circle relative * to the registration point of the parent display object(in * pixels). * @param radius The radius of the circle(in pixels). */ public drawCircle(x: number, y: number, radius: number): void { this._drawingDirty = true; //var radius2=radius*1.065; if (this._active_fill_path) { this._active_fill_path.moveTo(x, y); let r = radius; if (this._active_stroke_path) r -= (> this._active_stroke_path.style).thickness / 2; GraphicsFactoryHelper.drawElipse(x, y, r, r, this._active_fill_path.verts, 0, 360, 5, false); } if (this._active_stroke_path) GraphicsFactoryHelper.drawElipseStrokes(x, y, radius, radius, this._active_stroke_path , 0, 360, 2); this.invalidate(); } /** * Draws an ellipse. Set the line style, fill, or both before you call the * drawEllipse() method, by calling the * linestyle(), lineGradientStyle(), * beginFill(), beginGradientFill(), or * beginBitmapFill() method. * * @param x The x location of the top-left of the bounding-box of * the ellipse relative to the registration point of the parent * display object(in pixels). * @param y The y location of the top left of the bounding-box of * the ellipse relative to the registration point of the parent * display object(in pixels). * @param width The width of the ellipse(in pixels). * @param height The height of the ellipse(in pixels). */ public drawEllipse(x: number, y: number, width: number, height: number): void { this._drawingDirty = true; width /= 2; height /= 2; x += width; y += height; if (this._active_fill_path != null) { this._active_fill_path.moveTo(x, y); let w = width; let h = height; if (this._active_stroke_path != null) { w -= (> this._active_stroke_path.style).thickness / 2; h -= (> this._active_stroke_path.style).thickness / 2; } GraphicsFactoryHelper.drawElipse(x, y, w, h, this._active_fill_path.verts, 0, 360, 6, false); } if (this._active_stroke_path != null) { GraphicsFactoryHelper.drawElipseStrokes(x, y, width, height, this._active_stroke_path , 0, 360, 2); } this.invalidate(); } /** * Submits a series of IGraphicsData instances for drawing. This method * accepts a Vector containing objects including paths, fills, and strokes * that implement the IGraphicsData interface. A Vector of IGraphicsData * instances can refer to a part of a shape, or a complex fully defined set * of data for rendering a complete shape. * *

Graphics paths can contain other graphics paths. If the * graphicsData Vector includes a path, that path and all its * sub-paths are rendered during this operation.

* */ public drawGraphicsData(graphicsData: Array): void { /* for (var i:number=0; idrawPath() * method uses vector arrays to consolidate individual moveTo(), * lineTo(), and curveTo() drawing commands into a * single call. The drawPath() method parameters combine drawing * commands with x- and y-coordinate value pairs and a drawing direction. The * drawing commands are values from the GraphicsPathCommand class. The x- and * y-coordinate value pairs are Numbers in an array where each pair defines a * coordinate location. The drawing direction is a value from the * GraphicsPathWinding class. * *

Generally, drawings render faster with drawPath() than * with a series of individual lineTo() and * curveTo() methods.

* *

The drawPath() method uses a uses a floating computation * so rotation and scaling of shapes is more accurate and gives better * results. However, curves submitted using the drawPath() * method can have small sub-pixel alignment errors when used in conjunction * with the lineTo() and curveTo() methods.

* *

The drawPath() method also uses slightly different rules * for filling and drawing lines. They are:

* *
    *
  • When a fill is applied to rendering a path: *
      *
    • A sub-path of less than 3 points is not rendered.(But note that the * stroke rendering will still occur, consistent with the rules for strokes * below.)
    • *
    • A sub-path that isn't closed(the end point is not equal to the * begin point) is implicitly closed.
    • *
    *
  • *
  • When a stroke is applied to rendering a path: *
      *
    • The sub-paths can be composed of any number of points.
    • *
    • The sub-path is never implicitly closed.
    • *
    *
  • *
* * @param winding Specifies the winding rule using a value defined in the * GraphicsPathWinding class. */ public drawPath(commands: Int32Array, data: Float64Array, winding: GraphicsPathWinding): void { this._drawingDirty = true; //shapeAJS.queuePath(allPaths[i], null); // segment.serializeAJS(shape, null, { x: 0, y: 0}); const commandsCount = commands.length; let dataPosition; if (this._active_fill_path) this._drawPathInternal(this._active_fill_path, commands, data, winding); if (this._active_stroke_path) this._drawPathInternal(this._active_stroke_path, commands, data, winding); this.invalidate(); } private _drawPathInternal(path: GraphicsPath, commands: Int32Array, data: Float64Array, winding: GraphicsPathWinding) { let dataPosition = 0; for (let i = 0; i < commands.length; i++) { switch (commands[i]) { case GraphicsPathCommand.MOVE_TO: path.moveTo(data[dataPosition], data[dataPosition + 1]); dataPosition += 2; break; case GraphicsPathCommand.LINE_TO: path.lineTo(data[dataPosition], data[dataPosition + 1]); dataPosition += 2; break; case GraphicsPathCommand.CURVE_TO: path.curveTo(data[dataPosition], data[dataPosition + 1],data[dataPosition + 2], data[dataPosition + 3]); dataPosition += 4; break; case GraphicsPathCommand.NO_OP: default: } } } /** * Draws a rectangle. Set the line style, fill, or both before you call the * drawRect() method, by calling the linestyle(), * lineGradientStyle(), beginFill(), * beginGradientFill(), or beginBitmapFill() * method. * * @param x A number indicating the horizontal position relative to the * registration point of the parent display object(in pixels). * @param y A number indicating the vertical position relative to the * registration point of the parent display object(in pixels). * @param width The width of the rectangle(in pixels). * @param height The height of the rectangle(in pixels). * @throws ArgumentError If the width or height * parameters are not a number * (Number.NaN). */ public drawRect(x: number, y: number, width: number, height: number): void { this._drawingDirty = true; if (this._active_fill_path != null) { this._active_fill_path.moveTo(x, y); /* this._active_fill_path.lineTo(x+width, y); this._active_fill_path.lineTo(x+width, y+height); this._active_fill_path.lineTo(x, y+height); this._active_fill_path.lineTo(x, y); */ let w: number = width; let h: number = height; let t: number = 0; if (this._active_stroke_path != null) { t = (> this._active_stroke_path.style).thickness / 2; w -= (> this._active_stroke_path.style).thickness; h -= (> this._active_stroke_path.style).thickness; } GraphicsFactoryHelper.addTriangle( x + t, y + h + t, x + t, y + t, x + w + t, y + t, 0, this._active_fill_path.verts, false); GraphicsFactoryHelper.addTriangle( x + t, y + h + t, x + t + w, y + t, x + w + t, y + h + t, 0, this._active_fill_path.verts, false); } if (this._active_stroke_path != null) { this._active_stroke_path.moveTo(x, y); //var t:number=(this._active_stroke_path.style).thickness/2; /* eslint-disable */ // todo: respect Jointstyle here (?) /* GraphicsFactoryHelper.addTriangle(x-t, y+height+t, x-t, y-t, x+t, y+t, 0, this._active_stroke_path.verts, false); GraphicsFactoryHelper.addTriangle(x-t, y+height+t, x+t, y+height-t, x+t, y+t, 0, this._active_stroke_path.verts, false); GraphicsFactoryHelper.addTriangle(x-t, y-t, x+width+t, y-t, x+t, y+t, 0, this._active_stroke_path.verts, false); GraphicsFactoryHelper.addTriangle(x+t, y+t, x+width+t, y-t, x+width-t, y+t, 0, this._active_stroke_path.verts, false); GraphicsFactoryHelper.addTriangle(x+width-t, y+height-t, x+width-t, y+t, x+width+t, y+height+t, 0, this._active_stroke_path.verts, false); GraphicsFactoryHelper.addTriangle(x+width+t, y+height+t, x+width+t, y-t, x+width-t, y+t, 0, this._active_stroke_path.verts, false); GraphicsFactoryHelper.addTriangle(x-t, y+height+t, x+width+t, y+height+t, x+t, y+height-t, 0, this._active_stroke_path.verts, false); GraphicsFactoryHelper.addTriangle(x+t, y+height-t, x+width+t, y+height+t, x+width-t, y+height-t, 0, this._active_stroke_path.verts, false); */ /* eslint-enable */ this._active_stroke_path.lineTo(x + width, y); this._active_stroke_path.lineTo(x + width, y + height); this._active_stroke_path.lineTo(x, y + height); this._active_stroke_path.lineTo(x, y); } this.invalidate(); } /** * Draws a rounded rectangle. Set the line style, fill, or both before you * call the drawRoundRect() method, by calling the * linestyle(), lineGradientStyle(), * beginFill(), beginGradientFill(), or * beginBitmapFill() method. * * @param x A number indicating the horizontal position relative * to the registration point of the parent display * object(in pixels). * @param y A number indicating the vertical position relative to * the registration point of the parent display object * (in pixels). * @param width The width of the round rectangle(in pixels). * @param height The height of the round rectangle(in pixels). * @param ellipseWidth The width of the ellipse used to draw the rounded * corners(in pixels). * @param ellipseHeight The height of the ellipse used to draw the rounded * corners(in pixels). Optional; if no value is * specified, the default value matches that provided * for the ellipseWidth parameter. * @throws ArgumentError If the width, height, * ellipseWidth or * ellipseHeight parameters are not a * number(Number.NaN). */ public drawRoundRect( x: number, y: number, width: number, height: number, ellipseWidth: number, ellipseHeight: number = NaN): void { this._drawingDirty = true; if (isNaN(ellipseHeight)) { ellipseHeight = ellipseWidth; } let w: number = width; let h: number = height; const ew: number = ellipseWidth / 2; const eh: number = ellipseHeight / 2; let t: number = 0; if (this._active_fill_path != null) { this._active_fill_path.moveTo(x, y); if (this._active_stroke_path != null) { t = (> this._active_stroke_path.style).thickness / 2; w -= (> this._active_stroke_path.style).thickness; h -= (> this._active_stroke_path.style).thickness; } /* eslint-disable */ GraphicsFactoryHelper.addTriangle(x + t,y + h - eh, x + t,y + eh, x + w - t, y + eh,0, this._active_fill_path.verts, false); GraphicsFactoryHelper.addTriangle(x + t,y + h - eh, x + w - t,y + eh,x + w - t, y + h - eh, 0, this._active_fill_path.verts, false); GraphicsFactoryHelper.addTriangle(x + ew,y + t, x + w - ew, y + eh,x + ew,y + eh, 0, this._active_fill_path.verts, false); GraphicsFactoryHelper.addTriangle(x + ew,y + t, x + w - ew, y + t, x + w - ew,y + eh,0, this._active_fill_path.verts, false); GraphicsFactoryHelper.addTriangle(x + ew,y + h - eh, x + w - ew, y + h - t,x + ew,y + h - t, 0, this._active_fill_path.verts, false); GraphicsFactoryHelper.addTriangle(x + ew,y + h - eh, x + w - ew, y + h - eh, x + w - ew,y + h - t,0, this._active_fill_path.verts, false); GraphicsFactoryHelper.drawElipse(x + ew,y + eh, ew - t, eh - t, this._active_fill_path.verts, 180, 270, 5, false); GraphicsFactoryHelper.drawElipse(x + w - ew,y + eh, ew - t, eh - t, this._active_fill_path.verts, 270, 360, 5, false); GraphicsFactoryHelper.drawElipse(x + w - ew,y + h - eh, ew - t, eh - t, this._active_fill_path.verts, 0, 90, 5, false); GraphicsFactoryHelper.drawElipse(x + ew,y + h - eh, ew - t, eh - t, this._active_fill_path.verts, 90, 180, 5, false); /* eslint-enable */ } if (this._active_stroke_path != null) { this._active_stroke_path.moveTo(x + ew, y); /* eslint-disable */ this._active_stroke_path.lineTo(x + w - ew, y); GraphicsFactoryHelper.drawElipseStrokes(x + w - ew, y + eh, ew, eh, this._active_stroke_path, 270, 360, 2); this._active_stroke_path.lineTo(x + w, y + h - eh); GraphicsFactoryHelper.drawElipseStrokes(x + w - ew, y + h - eh, ew, eh, this._active_stroke_path, 0, 90, 2); this._active_stroke_path.lineTo(x + ew, y + h); GraphicsFactoryHelper.drawElipseStrokes(x + ew, y + h - eh, ew, eh, this._active_stroke_path, 90, 180, 2); this._active_stroke_path.lineTo(x, y + eh); GraphicsFactoryHelper.drawElipseStrokes(x + ew, y + eh, ew, eh, this._active_stroke_path, 180, 270, 2); /* eslint-enable */ } this.invalidate(); } public drawRoundRectComplex( x: number, y: number, width: number, height: number, topLeftRadius: number, topRightRadius: number, bottomLeftRadius: number, bottomRightRadius: number) { let w: number = width; let h: number = height; const tl: number = topLeftRadius; const tr: number = topRightRadius; const bl: number = bottomLeftRadius; const br: number = bottomRightRadius; this._drawingDirty = true; let t: number = 0; if (this._active_fill_path != null) { this._active_fill_path.moveTo(x, y); if (this._active_stroke_path != null) { t = (> this._active_stroke_path.style).thickness / 2; w -= (> this._active_stroke_path.style).thickness; h -= (> this._active_stroke_path.style).thickness; } /* eslint-disable */ GraphicsFactoryHelper.addTriangle(x + tl,y + tl, x + w - tr, y + tr, x + w - br, y + h - br, 0, this._active_fill_path.verts, false); GraphicsFactoryHelper.addTriangle(x + tl,y + tl, x + w - br, y + h - br, x + bl, y + h - bl, 0, this._active_fill_path.verts, false); GraphicsFactoryHelper.addTriangle(x + t,y + tl,x + tl,y + tl, x + t,y + h - bl, 0, this._active_fill_path.verts, false); GraphicsFactoryHelper.addTriangle(x + tl,y + tl, x + t,y + h - bl, x + bl,y + h - bl, 0, this._active_fill_path.verts, false); GraphicsFactoryHelper.addTriangle(x + tl,y + t,x + tl,y + tl, x + w - tr,y + t, 0, this._active_fill_path.verts, false); GraphicsFactoryHelper.addTriangle(x + tl,y + tl, x + w - tr,y + tr, x + w - tr,y + t, 0, this._active_fill_path.verts, false); GraphicsFactoryHelper.addTriangle(x + w - t,y + tr,x + w - tr,y + tr, x + w - t,y + h - br, 0, this._active_fill_path.verts, false); GraphicsFactoryHelper.addTriangle(x + w - tr,y + tr, x + w - br,y + h - br, x + w - t,y + h - br, 0, this._active_fill_path.verts, false); GraphicsFactoryHelper.addTriangle(x + bl,y + h - t, x + w - br,y + h - t,x + bl,y + h - bl, 0, this._active_fill_path.verts, false); GraphicsFactoryHelper.addTriangle(x + bl,y + h - bl, x + w - br,y + h - t, x + w - br,y + h - br, 0, this._active_fill_path.verts, false); GraphicsFactoryHelper.drawElipse(x + tl,y + tl, tl - t, tl - t, this._active_fill_path.verts, 180, 270, 5, false); GraphicsFactoryHelper.drawElipse(x + w - tr,y + tr, tr - t, tr - t, this._active_fill_path.verts, 270, 360, 5, false); GraphicsFactoryHelper.drawElipse(x + w - br,y + h - br, br - t, br - t, this._active_fill_path.verts, 0, 90, 5, false); GraphicsFactoryHelper.drawElipse(x + bl,y + h - bl, bl - t, bl - t, this._active_fill_path.verts, 90, 180, 5, false); /* eslint-enable */ } if (this._active_stroke_path != null) { this._active_stroke_path.moveTo(x, y); console.warn('[Graphics] - drawRoundRectComplex for strokes currently disabled'); /* eslint-disable */ /* GraphicsFactoryHelper.addTriangle(x - t, y + h - bl, x - t, y + tl, x + t, y + tl, 0, this._active_stroke_path.verts, false); GraphicsFactoryHelper.addTriangle(x - t, y + h - bl, x + t, y + h - bl, x + t, y + tl, 0, this._active_stroke_path.verts, false); GraphicsFactoryHelper.addTriangle(x + tl, y - t, x + w - tr, y - t, x + tr, y + t, 0, this._active_stroke_path.verts, false); GraphicsFactoryHelper.addTriangle(x + tl, y + t, x + w - tr, y - t, x + w - tr, y + t, 0, this._active_stroke_path.verts, false); GraphicsFactoryHelper.addTriangle(x + w - t, y + h - br, x + w - t, y + tr, x + w + t, y + h - br, 0, this._active_stroke_path.verts, false); GraphicsFactoryHelper.addTriangle(x + w + t, y + h - br, x + w + t, y + tr, x + w - t, y + tr, 0, this._active_stroke_path.verts, false); GraphicsFactoryHelper.addTriangle(x + bl, y + h + t, x + w - br, y + h + t, x + bl, y + h - t, 0, this._active_stroke_path.verts, false); GraphicsFactoryHelper.addTriangle(x + bl, y + h - t, x + w - br, y + h + t, x + w - br, y + h - t, 0, this._active_stroke_path.verts, false); GraphicsFactoryHelper.drawElipseStrokes(x + tl,y + tl, tl, tl, this._active_stroke_path.verts, 180, 270, 5, t, false); GraphicsFactoryHelper.drawElipseStrokes(x + w - tr,y + tr, tr, tr, this._active_stroke_path.verts, 270, 360, 5, t, false); GraphicsFactoryHelper.drawElipseStrokes(x + w - br,y + h - br, br, br, this._active_stroke_path.verts, 0, 90, 5, t, false); GraphicsFactoryHelper.drawElipseStrokes(x + bl,y + h - bl, bl, bl, this._active_stroke_path.verts, 90, 180, 5, t, false); */ /* eslint-enable */ } this.invalidate(); } /** * Renders a set of triangles, typically to distort bitmaps and give them a * three-dimensional appearance. The drawTriangles() method maps * either the current fill, or a bitmap fill, to the triangle faces using a * set of(u,v) coordinates. * *

Any type of fill can be used, but if the fill has a transform matrix * that transform matrix is ignored.

* *

A uvtData parameter improves texture mapping when a * bitmap fill is used.

* * @param culling Specifies whether to render triangles that face in a * specified direction. This parameter prevents the rendering * of triangles that cannot be seen in the current view. This * parameter can be set to any value defined by the * TriangleCulling class. */ public drawTriangles( vertices: Array, indices: Array = null, uvtData: Array = null, culling: TriangleCulling = null): void { this._drawingDirty = true; if (this._active_fill_path != null) { //todo } if (this._active_stroke_path != null) { //todo } } /** * Applies a fill to the lines and curves that were added since the last call * to the beginFill(), beginGradientFill(), or * beginBitmapFill() method. Flash uses the fill that was * specified in the previous call to the beginFill(), * beginGradientFill(), or beginBitmapFill() * method. If the current drawing position does not equal the previous * position specified in a moveTo() method and a fill is * defined, the path is closed with a line and then filled. * */ public endFill(): void { if (this._active_stroke_path && this._active_fill_path) this._active_stroke_path.forceClose = true; this._endFillInternal(true); } private _endFillInternal(clear = false) { //execute any queued shapetags if (this._queuedShapeTags.length) { const localQueue = this._queuedShapeTags; const len = localQueue.length; this._queuedShapeTags = []; for (let i: number = 0; i < len; i++) this.convertRecordsToShapeData(localQueue[i]); localQueue.length = 0; } GraphicsFactoryFills.draw_pathes(this, clear); GraphicsFactoryStrokes.draw_pathes(this, clear); if (clear) { //reset fill style this._fillStyle = null; //create a new line path if needed this._updateLinePath(); } this._drawingDirty = false; } /** * Specifies a bitmap to use for the line stroke when drawing lines. * *

The bitmap line style is used for subsequent calls to Graphics methods * such as the lineTo() method or the drawCircle() * method. The line style remains in effect until you call the * lineStyle() or lineGradientStyle() methods, or * the lineBitmapStyle() method again with different parameters. *

* *

You can call the lineBitmapStyle() method in the middle of * drawing a path to specify different styles for different line segments * within a path.

* *

Call the lineStyle() method before you call the * lineBitmapStyle() method to enable a stroke, or else the * value of the line style is undefined.

* *

Calls to the clear() method set the line style back to * undefined.

* * @param bitmap The bitmap to use for the line stroke. * @param matrix An optional transformation matrix as defined by the * flash.geom.Matrix class. The matrix can be used to scale or * otherwise manipulate the bitmap before applying it to the * line style. * @param repeat Whether to repeat the bitmap in a tiled fashion. * @param smooth Whether smoothing should be applied to the bitmap. */ public lineBitmapStyle( bitmap: BitmapImage2D, matrix: Matrix = null, repeat: boolean = true, smooth: boolean = false): void { if (this._lineStyle) { this._lineStyle = this._lineStyle.clone(); this._lineStyle.fillStyle = new GraphicsFillStyle( new BitmapFillStyle( bitmap, matrix, repeat, smooth) ); } this._updateLinePath(); } /** * Specifies a gradient to use for the stroke when drawing lines. * *

The gradient line style is used for subsequent calls to Graphics * methods such as the lineTo() methods or the * drawCircle() method. The line style remains in effect until * you call the lineStyle() or lineBitmapStyle() * methods, or the lineGradientStyle() method again with * different parameters.

* *

You can call the lineGradientStyle() method in the middle * of drawing a path to specify different styles for different line segments * within a path.

* *

Call the lineStyle() method before you call the * lineGradientStyle() method to enable a stroke, or else the * value of the line style is undefined.

* *

Calls to the clear() method set the line style back to * undefined.

* * @param type A value from the GradientType class that * specifies which gradient type to use, either * GradientType.LINEAR or GradientType.RADIAL. * @param colors An array of RGB hexadecimal color values used * in the gradient; for example, red is 0xFF0000, * blue is 0x0000FF, and so on. You can specify * up to 15 colors. For each color, specify a * corresponding value in the alphas and ratios * parameters. * @param alphas An array of alpha values for the corresponding * colors in the colors array; valid values are 0 * to 1. If the value is less than 0, the default * is 0. If the value is greater than 1, the * default is 1. * @param ratios An array of color distribution ratios; valid * values are 0-255. This value defines the * percentage of the width where the color is * sampled at 100%. The value 0 represents the * left position in the gradient box, and 255 * represents the right position in the gradient * box. * @param matrix A transformation matrix as defined by the * flash.geom.Matrix class. The flash.geom.Matrix * class includes a * createGradientBox() method, which * lets you conveniently set up the matrix for use * with the lineGradientStyle() * method. * @param spreadMethod A value from the SpreadMethod class that * specifies which spread method to use: * @param interpolationMethod A value from the InterpolationMethod class that * specifies which value to use. For example, * consider a simple linear gradient between two * colors(with the spreadMethod * parameter set to * SpreadMethod.REFLECT). The * different interpolation methods affect the * appearance as follows: * @param focalPointRatio A number that controls the location of the * focal point of the gradient. The value 0 means * the focal point is in the center. The value 1 * means the focal point is at one border of the * gradient circle. The value -1 means that the * focal point is at the other border of the * gradient circle. Values less than -1 or greater * than 1 are rounded to -1 or 1. The following * image shows a gradient with a * focalPointRatio of -0.75: */ public lineGradientStyle( type: GradientType, colors: Array, alphas: Array, ratios: Array, matrix: Matrix = null, spreadMethod: SpreadMethod = SpreadMethod.PAD, interpolationMethod: InterpolationMethod = InterpolationMethod.RGB, focalPointRatio: number = 0 ): void { if (this._lineStyle) { this._lineStyle = this._lineStyle.clone(); this._lineStyle.fillStyle = new GradientFillStyle( type, colors, alphas, ratios, matrix, spreadMethod, interpolationMethod, focalPointRatio ); } this._updateLinePath(); } /** * Specifies a shader to use for the line stroke when drawing lines. * *

The shader line style is used for subsequent calls to Graphics methods * such as the lineTo() method or the drawCircle() * method. The line style remains in effect until you call the * lineStyle() or lineGradientStyle() methods, or * the lineBitmapStyle() method again with different parameters. *

* *

You can call the lineShaderStyle() method in the middle of * drawing a path to specify different styles for different line segments * within a path.

* *

Call the lineStyle() method before you call the * lineShaderStyle() method to enable a stroke, or else the * value of the line style is undefined.

* *

Calls to the clear() method set the line style back to * undefined.

* * @param shader The shader to use for the line stroke. * @param matrix An optional transformation matrix as defined by the * flash.geom.Matrix class. The matrix can be used to scale or * otherwise manipulate the bitmap before applying it to the * line style. */ // public lineShaderStyle(shader:Shader, matrix:Matrix = null) // { // // } /** * Specifies a line style used for subsequent calls to Graphics methods such * as the lineTo() method or the drawCircle() * method. The line style remains in effect until you call the * lineGradientStyle() method, the * lineBitmapStyle() method, or the lineStyle() * method with different parameters. * *

You can call the lineStyle() method in the middle of * drawing a path to specify different styles for different line segments * within the path.

* *

Note: Calls to the clear() method set the line * style back to undefined.

* *

Note: Flash Lite 4 supports only the first three parameters * (thickness, color, and alpha).

* * @param thickness An integer that indicates the thickness of the line in * points; valid values are 0-255. If a number is not * specified, or if the parameter is undefined, a line is * not drawn. If a value of less than 0 is passed, the * default is 0. The value 0 indicates hairline * thickness; the maximum thickness is 255. If a value * greater than 255 is passed, the default is 255. * @param color A hexadecimal color value of the line; for example, * red is 0xFF0000, blue is 0x0000FF, and so on. If a * value is not indicated, the default is 0x000000 * (black). Optional. * @param alpha A number that indicates the alpha value of the color * of the line; valid values are 0 to 1. If a value is * not indicated, the default is 1(solid). If the value * is less than 0, the default is 0. If the value is * greater than 1, the default is 1. * @param pixelHinting(Not supported in Flash Lite 4) A Boolean value that * specifies whether to hint strokes to full pixels. This * affects both the position of anchors of a curve and * the line stroke size itself. With * pixelHinting set to true, * line widths are adjusted to full pixel widths. With * pixelHinting set to false, * disjoints can appear for curves and straight lines. * For example, the following illustrations show how * Flash Player or Adobe AIR renders two rounded * rectangles that are identical, except that the * pixelHinting parameter used in the * lineStyle() method is set differently * (the images are scaled by 200%, to emphasize the * difference): * *

If a value is not supplied, the line does not use * pixel hinting.

* @param scaleMode (Not supported in Flash Lite 4) A value from the * LineScaleMode class that specifies which scale mode to * use: *
    *
  • LineScaleMode.NORMAL - Always * scale the line thickness when the object is scaled * (the default).
  • *
  • LineScaleMode.NONE - Never scale * the line thickness.
  • *
  • LineScaleMode.VERTICAL - Do not * scale the line thickness if the object is scaled * vertically only. For example, consider the * following circles, drawn with a one-pixel line, and * each with the scaleMode parameter set to * LineScaleMode.VERTICAL. The circle on the * left is scaled vertically only, and the circle on the * right is scaled both vertically and horizontally: *
  • *
  • LineScaleMode.HORIZONTAL - Do not * scale the line thickness if the object is scaled * horizontally only. For example, consider the * following circles, drawn with a one-pixel line, and * each with the scaleMode parameter set to * LineScaleMode.HORIZONTAL. The circle on * the left is scaled horizontally only, and the circle * on the right is scaled both vertically and * horizontally:
  • *
* @param caps (Not supported in Flash Lite 4) A value from the * CapsStyle class that specifies the type of caps at the * end of lines. Valid values are: * CapsStyle.NONE, * CapsStyle.ROUND, and * CapsStyle.SQUARE. If a value is not * indicated, Flash uses round caps. * *

For example, the following illustrations show the * different capsStyle settings. For each * setting, the illustration shows a blue line with a * thickness of 30(for which the capsStyle * applies), and a superimposed black line with a * thickness of 1(for which no capsStyle * applies):

* @param joints (Not supported in Flash Lite 4) A value from the * JointStyle class that specifies the type of joint * appearance used at angles. Valid values are: * JointStyle.BEVEL, * JointStyle.MITER, and * JointStyle.ROUND. If a value is not * indicated, Flash uses round joints. * *

For example, the following illustrations show the * different joints settings. For each * setting, the illustration shows an angled blue line * with a thickness of 30(for which the * jointStyle applies), and a superimposed * angled black line with a thickness of 1(for which no * jointStyle applies):

* *

Note: For joints set to * JointStyle.MITER, you can use the * miterLimit parameter to limit the length * of the miter.

* @param miterLimit (Not supported in Flash Lite 4) A number that * indicates the limit at which a miter is cut off. Valid * values range from 1 to 255(and values outside that * range are rounded to 1 or 255). This value is only * used if the jointStyle is set to * "miter". The miterLimit * value represents the length that a miter can extend * beyond the point at which the lines meet to form a * joint. The value expresses a factor of the line * thickness. For example, with a * miterLimit factor of 2.5 and a * thickness of 10 pixels, the miter is cut * off at 25 pixels. * *

For example, consider the following angled lines, * each drawn with a thickness of 20, but * with miterLimit set to 1, 2, and 4. * Superimposed are black reference lines showing the * meeting points of the joints:

* *

Notice that a given miterLimit value * has a specific maximum angle for which the miter is * cut off. The following table lists some examples:

*/ public lineStyle( thickness: number = NaN, color: number /*int*/ = 0, alpha: number = 1, pixelHinting: boolean = false, scaleMode: LineScaleMode = null, capstyle: CapsStyle = CapsStyle.NONE, jointstyle: JointStyle = JointStyle.MITER, miterLimit: number = 100 ): void { if (isNaN(thickness)) { this._lineStyle = null; this._updateLinePath(); return; } if (thickness == 0) { thickness = 0.05; scaleMode = LineScaleMode.HAIRLINE; } const valid = thickness > 0 && alpha > 0; this._lineStyle = valid ? new GraphicsStrokeStyle( new SolidFillStyle(color, alpha), thickness, jointstyle, capstyle, miterLimit ) : null; this._updateLinePath(); } /** * Draws a line using the current line style from the current drawing * position to(x, y); the current drawing position * is then set to(x, y). If the display object in * which you are drawing contains content that was created with the Flash * drawing tools, calls to the lineTo() method are drawn * underneath the content. If you call lineTo() before any calls * to the moveTo() method, the default position for the current * drawing is(0, 0). If any of the parameters are missing, this * method fails and the current drawing position is not changed. * * @param x A number that indicates the horizontal position relative to the * registration point of the parent display object(in pixels). * @param y A number that indicates the vertical position relative to the * registration point of the parent display object(in pixels). */ public lineTo(x: number, y: number): void { this._drawingDirty = true; if (this._active_fill_path) this._active_fill_path.lineTo(x, y); if (this._active_stroke_path) this._active_stroke_path.lineTo(x, y); this._current_position.x = x; this._current_position.y = y; this.invalidate(); } /** * Moves the current drawing position to(x, y). If * any of the parameters are missing, this method fails and the current * drawing position is not changed. * * @param x A number that indicates the horizontal position relative to the * registration point of the parent display object(in pixels). * @param y A number that indicates the vertical position relative to the * registration point of the parent display object(in pixels). */ public moveTo(x: number, y: number): void { this._drawingDirty = true; if (this._active_fill_path) this._active_fill_path.moveTo(x, y); if (this._active_stroke_path) this._active_stroke_path.moveTo(x, y); this._current_position.x = x; this._current_position.y = y; this.invalidate(); } public queueShapeTag(shapeTag: ShapeTag): void { this._queuedShapeTags.push(shapeTag); this._drawingDirty = true; return; } private _updateFillPath() { if (this._fillStyle) { if (this._active_fill_path == null || this._active_fill_path.style != this._fillStyle) { this._active_fill_path = new GraphicsPath(); this._active_fill_path.style = this._fillStyle; this._queued_fill_pathes.push(this._active_fill_path); //auto-add move command if starting position is not zero if (this._current_position.x != 0 || this._current_position.y != 0) this._active_fill_path.moveTo(this._current_position.x, this._current_position.y); } } else { this._active_fill_path = null; } } private _updateLinePath() { if (this._lineStyle) { if (this._active_stroke_path == null || this._active_stroke_path.style != this._lineStyle) { this._active_stroke_path = new GraphicsPath(); this._active_stroke_path.style = this._lineStyle; this._queued_stroke_pathes.push(this._active_stroke_path); //auto-add move command if starting position is not zero if (this._current_position.x != 0 || this._current_position.y != 0) this._active_stroke_path.moveTo(this._current_position.x, this._current_position.y); } } else { this._active_stroke_path = null; } } private _addShapes(shapes: Array>, cloneShapes: boolean = false): void { let shape: Shape; const len: number = shapes.length; for (let i: number = 0; i < len; i++) { shape = shapes[i]; if (cloneShapes) { shape = Shape.getShape( shape.elements, shape.material, shape.style, shape.count, shape.offset); } shape.particleCollection = shapes[i].particleCollection; this._shapes.push(shape); shape.usages++; } this.invalidate(); } public _isShapeMaterial(material: IMaterial): boolean { const len: number = this._shapes.length; for (let i: number = 0; i < len; i++) if (material == this._shapes[i].material) return true; return false; } /* * Converts records from the space-optimized format they're stored in to a * format that's more amenable to fast rendering. * * See http://blogs.msdn.com/b/mswanson/archive/2006/02/27/539749.aspx and * http://wahlers.com.br/claus/blog/hacking-swf-1-shapes-in-flash/ for details. */ public convertRecordsToShapeData(tag: ShapeTag, supressFill = false): void { // run parser if (tag.needParse) { //console.log("Run lazy Graphics parser", tag.id); //console.time('Run lazy Graphics parser:' + tag.id); tag.lazyParser(); //console.timeEnd('Run lazy Graphics parser:' + tag.id); } const records: ShapeRecord[] = tag.records; const fillStyles: FillStyle[] = tag.fillStyles; const lineStyles: LineStyle[] = tag.lineStyles; const recordsMorph: ShapeRecord[] = tag.recordsMorph || null; const isMorph: boolean = recordsMorph !== null; const factory: IMaterialFactory = tag.factory; if (this.tryOptimiseSigleImage && records.length === 5 && fillStyles.length === 2 && (fillStyles[0].type >= FillType.ClippedBitmap) ) { //1 style is trash, second is needed const style = StyleUtils.processStyle(tag.fillStyles[1], false, false, factory); const bounds = tag.fillBounds || tag.lineBounds; this.addShapeInternal(Graphics.getShapeForBitmapStyle(style, bounds)); return; } let fillPaths = StyleUtils.createPathsList(fillStyles, false, !!recordsMorph, factory); let linePaths = StyleUtils.createPathsList(lineStyles, true, !!recordsMorph, factory); let styles = { fill0: 0, fill1: 0, line: 0 }; interface IPathElement { segment?: PathSegment | undefined, path: SegmentedPath | undefined, } const psPool: Array = []; // Fill- and line styles can be added by style change records in the middle of // a shape records list. This also causes the previous paths to be treated as // a group, so the lines don't get moved on top of any following fills. // To support this, we just append all current fill and line paths to a list // when new styles are introduced. let allPaths: SegmentedPath[] = []; const numRecords = records.length; let x: number = 0; let y: number = 0; let mX: number = 0; let mY: number = 0; let morphRecord: ShapeRecord; //console.log("numRecords", numRecords); for (let i = 0, j = 0; i < numRecords; i++) { const record: ShapeRecord = records[i]; //console.log("record", i, record.type); // type 0 is a StyleChange record if (record.type === 0) { // reset segment pool for (let i = 0; i < 3; i++) { psPool[i] = undefined; } if (record.flags & ShapeRecordFlags.HasNewStyles) { if (!allPaths) { allPaths = []; } Array_push.apply(allPaths, fillPaths); fillPaths = StyleUtils.createPathsList(record.fillStyles, false, isMorph, factory); Array_push.apply(allPaths, linePaths); linePaths = StyleUtils.createPathsList(record.lineStyles, true, isMorph, factory); styles = { fill0: 0, fill1: 0, line: 0 }; } if (record.flags & ShapeRecordFlags.HasFillStyle0) { styles.fill0 = record.fillStyle0; } if (record.flags & ShapeRecordFlags.HasFillStyle1) { styles.fill1 = record.fillStyle1; } if (record.flags & ShapeRecordFlags.HasLineStyle) { styles.line = record.lineStyle; } // reset all segments and pathes to null, and than reset them based on new styles // path1 = path2 = pathL=null; // segment1 = segment2 = segmentL=null; if (styles.fill0) { psPool[0] = { path: fillPaths[styles.fill0 - 1], }; //path1 = fillPaths[styles.fill0 - 1]; } if (styles.fill1) { psPool[1] = { path: fillPaths[styles.fill1 - 1], }; //path2 = fillPaths[styles.fill1 - 1]; } if (styles.line) { psPool[2] = { path: linePaths[styles.line - 1], }; //pathL = linePaths[styles.line - 1]; } if (record.flags & ShapeRecordFlags.Move) { x = record.moveX | 0; y = record.moveY | 0; // When morphed, StyleChangeRecords/MoveTo might not have a // corresponding record in the start or end shape -- // processing morphRecord below before converting type 1 records. } // if a segment1 has same fill on both sides, we want to ignore this segment1 for fills if (styles.fill1 && styles.fill0 && styles.fill0 == styles.fill1) { psPool[0] = psPool[1] = undefined; } if (isMorph) { morphRecord = recordsMorph[j++]; if (morphRecord.type === 0) { mX = morphRecord.moveX | 0; mY = morphRecord.moveY | 0; } else { //mX = x; //mY = y; j--; } } for (let i = 0; i < 3; i++) { const entry = psPool[i]; if (!entry) { continue; } entry.segment = PathSegment.FromDefaults(isMorph); entry.path.addSegment(entry.segment); if (!isMorph) { entry.segment.moveTo(x, y); //console.log("segment1.moveTo" ,x/20, y/20); } else { entry.segment.morphMoveTo(x, y, mX, mY); } } // eslint-disable-next-line brace-style } // type 1 is a StraightEdge or CurvedEdge record else { assert(record.type === 1); if (isMorph) { morphRecord = recordsMorph[j++]; // An invalid SWF might contain a move in the EndEdges list where the // StartEdges list contains an edge. The Flash Player seems to skip it, // so we do, too. while (morphRecord && morphRecord.type === 0) { morphRecord = recordsMorph[j++]; } // The EndEdges list might be shorter than the StartEdges list. Reuse // start edges as end edges in that case. if (!morphRecord) { morphRecord = record; } } if (record.flags & ShapeRecordFlags.IsStraight && (!isMorph || (morphRecord.flags & ShapeRecordFlags.IsStraight))) { x += record.deltaX | 0; y += record.deltaY | 0; if (isMorph) { mX += morphRecord.deltaX | 0; mY += morphRecord.deltaY | 0; } for (let i = 0; i < 3; i++) { const entry = psPool[i]; if (!entry) { continue; } if (!isMorph) { entry.segment.lineTo(x,y); } else { entry.segment.morphLineTo(x, y, mX, mY); } } } else { const data = this.transformCurve(record, x, y); x = data.x; y = data.y; let mData: {cx: number, cy: number, x: number, y: number}; if (isMorph) { mData = this.transformCurve(morphRecord, mX, mY); mX = mData.x; mY = mData.y; } for (let i = 0; i < 3; i++) { const e = psPool[i]; if (!e) { continue; } const s = e.segment; if (!isMorph) { s.curveTo(data.cx, data.cy, x, y); } else { s.morphCurveTo(data.cx, data.cy, x, y, mData.cx, mData.cy, mX, mY); } } } } } // applySegmentToStyles(segment1, styles, linePaths, fillPaths); // All current paths get appended to the allPaths list at the end. First fill, // then line paths. // allPaths = (allPaths || []).concat(fillPaths || [], linePaths || [], defaultPath || []); const sources = [allPaths, fillPaths, linePaths]; let shapeAJS: GraphicsPath; let morphShapeAJS: GraphicsPath; let current = 0; if (isMorph) { //shape.morphCoordinates = new Int32Array(shape.coordinates.length); //shape.morphStyles = new DataBuffer(16); this._start = []; this._end = []; for (let i = 0; current < sources.length ; i++) { if (!sources[current] || sources[current].length <= i) { current++; i = -1; continue; } //allPaths[i].serialize(shape); shapeAJS = new GraphicsPath(); morphShapeAJS = new GraphicsPath(); // should disable internal optimisation for regular shapes shapeAJS.morphSource = morphShapeAJS.morphSource = true; //shapeAJS.queuePath(allPaths[i], morphShapeAJS) sources[current][i].serializeAJS(shapeAJS, morphShapeAJS); this._start.push(shapeAJS); this._end.push(morphShapeAJS); } } else { for (let i = 0; current < sources.length; i++) { if (!sources[current] || sources[current].length <= i) { current++; i = -1; continue; } //console.log("allPaths", i, allPaths[i]); //allPaths[i].serialize(shape); shapeAJS = new GraphicsPath(); //shapeAJS.queuePath(allPaths[i], null); sources[current][i].serializeAJS(shapeAJS, null); //console.log("shapeAJS", shapeAJS); this.add_queued_path(shapeAJS, supressFill); } } } private transformCurve(record: ShapeRecord, x: number, y: number) { let cx: number, cy: number; if (!(record.flags & ShapeRecordFlags.IsStraight)) { cx = x + record.controlDeltaX | 0; cy = y + record.controlDeltaY | 0; x = cx + record.anchorDeltaX | 0; y = cy + record.anchorDeltaY | 0; } else { const deltaX = record.deltaX | 0; const deltaY = record.deltaY | 0; cx = x + (deltaX >> 1); cy = y + (deltaY >> 1); x += deltaX; y += deltaY; } return { cx, cy, x, y }; } }