@awayjs/graphics
Version:
AwayJS graphics classes
1,357 lines (1,131 loc) • 82.3 kB
text/typescript
import {
ArgumentError,
RangeError,
Point,
Matrix,
Matrix3D,
AssetBase,
Rectangle,
WeakAssetSet,
IAsset,
} from '@awayjs/core';
import { BitmapImage2D, ImageSampler } from '@awayjs/stage';
import { 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<Graphics> = new Array<Graphics>();
public static getShapeForBitmap (
bitmap: BitmapImage2D,
rect: Rectangle,
): Shape<TriangleElements> {
const mat = MaterialManager.getMaterialForBitmap(bitmap);
const style = mat.style;
style.sampler = new ImageSampler(false, true, false);
style.addSamplerAt(style.sampler, mat.getTextureAt(0));
return Shape.getShape<TriangleElements> (Shape.getTriangleElement(rect, false, true), mat, style);
}
public static getShapeForBitmapStyle (shapeStyle: ShapeStyle, flashBox: BBox): Shape<TriangleElements> {
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;
const texture = shapeStyle.material.getTextureAt(0);
const mat = MaterialManager.getMaterialForBitmap(<BitmapImage2D>shapeStyle.material.style.image);
const bitmapFillStyle = new BitmapFillStyle(
mat,
new Matrix(a, b, c ,d, tx, ty),
shapeStyle.repeat,
shapeStyle.smooth
);
const material = bitmapFillStyle.material;
//enforce image smooth style
const sampler = new ImageSampler(
bitmapFillStyle.repeat, bitmapFillStyle.smooth, shapeStyle.smooth);
material.style.sampler = sampler;
material.animateUVs = true;
style.addSamplerAt(sampler, texture);
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<GraphicsFillStyle<BitmapFillStyle>> = {};
private _queuedShapeTags: ShapeTag[] = [];
private _shapes: Array<Shape> = [];
private _queued_fill_pathes: GraphicsPath[] = [];
private _queued_stroke_pathes: GraphicsPath[] = [];
public _active_fill_path: GraphicsPath;
public _active_stroke_path: GraphicsPath;
private _lineStyle: GraphicsStrokeStyle<any>;
private _fillStyle: GraphicsFillStyle<any>;
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('Sprite');
public _start: GraphicsPath[];
public _end: GraphicsPath[];
/*private*/ _clearCount: number = 0;
private _internalShapesId: number[] = [];
private _rFillPool: ManagedPool<Shape> = new ManagedPool<Shape>(Shape, 100, false);
private _rStrokePool: ManagedPool<Shape> = new ManagedPool<Shape>(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<GraphicsPath> {
return this._queued_stroke_pathes;
}
public set queued_stroke_pathes(value: Array<GraphicsPath>) {
this._queued_stroke_pathes = value;
}
public get queued_fill_pathes(): Array<GraphicsPath> {
return this._queued_fill_pathes;
}
public set queued_fill_pathes(value: Array<GraphicsPath>) {
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<any>) {
this.addShape(shape);
this._internalShapesId.push(shape.id);
}
public addOwner(owner: IAsset): void {
this._owners.add(owner);
}
public removeOwner(owner: IAsset): void {
this._owners.remove(owner);
}
public invalidate(): void {
super.invalidate();
this._owners.forEach((asset: IAsset) => asset.invalidate());
}
/**
* Adds a GraphicBase wrapping a Elements.
*
* @param elements
*/
public addShape(shape: Shape<any>): 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(<any>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();
if (this._bitmapFillPool) {
for (const k in this._bitmapFillPool) {
this._bitmapFillPool[k].fillStyle.material.dispose();
}
this._bitmapFillPool = null;
}
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
* <code>beginFill()</code>, <code>beginBitmapFill()</code>,
* <code>beginGradientFill()</code>, or <code>beginShaderFill()</code>
* method. Calling the <code>clear()</code> method clears the fill.
*
* <p>The application renders the fill whenever three or more points are
* drawn, or when the <code>endFill()</code> method is called. </p>
*
* @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 <code>true</code>, the bitmap image repeats in a tiled
* pattern. If <code>false</code>, the bitmap image does not
* repeat, and the edges of the bitmap are used for any fill
* area that extends beyond the bitmap.
*
* <p>For example, consider the following bitmap(a 20 x
* 20-pixel checkerboard pattern):</p>
*
* <p>When <code>repeat</code> is set to <code>true</code>(as
* in the following example), the bitmap fill repeats the
* bitmap:</p>
*
* <p>When <code>repeat</code> is set to <code>false</code>,
* the bitmap fill uses the edge pixels for the fill area
* outside the bitmap:</p>
* @param smooth If <code>false</code>, upscaled bitmap images are rendered
* by using a nearest-neighbor algorithm and look pixelated. If
* <code>true</code>, 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<BitmapFillStyle>(
new BitmapFillStyle(
MaterialManager.getMaterialForBitmap(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 <code>lineTo()</code> or <code>drawCircle()</code>) use
* when drawing. The fill remains in effect until you call the
* <code>beginFill()</code>, <code>beginBitmapFill()</code>,
* <code>beginGradientFill()</code>, or <code>beginShaderFill()</code>
* method. Calling the <code>clear()</code> method clears the fill.
*
* <p>The application renders the fill whenever three or more points are
* drawn, or when the <code>endFill()</code> method is called.</p>
*
* @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<SolidFillStyle>(new SolidFillStyle(color, alpha));
this._updateFillPath();
}
/**
* Specifies a gradient fill used by subsequent calls to other Graphics
* methods(such as <code>lineTo()</code> or <code>drawCircle()</code>) for
* the object. The fill remains in effect until you call the
* <code>beginFill()</code>, <code>beginBitmapFill()</code>,
* <code>beginGradientFill()</code>, or <code>beginShaderFill()</code>
* method. Calling the <code>clear()</code> method clears the fill.
*
* <p>The application renders the fill whenever three or more points are
* drawn, or when the <code>endFill()</code> method is called. </p>
*
* @param type A value from the GradientType class that
* specifies which gradient type to use:
* <code>GradientType.LINEAR</code> or
* <code>GradientType.RADIAL</code>.
* @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
* <code>createGradientBox()</code> method, which
* lets you conveniently set up the matrix for use
* with the <code>beginGradientFill()</code>
* method.
* @param spreadMethod A value from the SpreadMethod class that
* specifies which spread method to use, either:
* <code>SpreadMethod.PAD</code>,
* <code>SpreadMethod.REFLECT</code>, or
* <code>SpreadMethod.REPEAT</code>.
*
* <p>For example, consider a simple linear
* gradient between two colors:</p>
*
* <p>This example uses
* <code>SpreadMethod.PAD</code> for the spread
* method, and the gradient fill looks like the
* following:</p>
*
* <p>If you use <code>SpreadMethod.REFLECT</code>
* for the spread method, the gradient fill looks
* like the following:</p>
*
* <p>If you use <code>SpreadMethod.REPEAT</code>
* for the spread method, the gradient fill looks
* like the following:</p>
* @param interpolationMethod A value from the InterpolationMethod class that
* specifies which value to use:
* <code>InterpolationMethod.LINEAR_RGB</code> or
* <code>InterpolationMethod.RGB</code>
*
* <p>For example, consider a simple linear
* gradient between two colors(with the
* <code>spreadMethod</code> parameter set to
* <code>SpreadMethod.REFLECT</code>). The
* different interpolation methods affect the
* appearance as follows: </p>
* @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 <code>focalPointRatio</code> set to 0.75:
* @throws ArgumentError If the <code>type</code> 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<GradientFillStyle>(
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
* <code>cubicCurveTo()</code> method are as follows:
*
* <ul>
* <li>The current drawing position is the first anchor point. </li>
* <li>The anchorX and anchorY parameters specify the second anchor point.
* </li>
* <li>The <code>controlX1</code> and <code>controlY1</code> parameters
* specify the first control point.</li>
* <li>The <code>controlX2</code> and <code>controlY2</code> parameters
* specify the second control point.</li>
* </ul>
*
* If you call the <code>cubicCurveTo()</code> method before calling the
* <code>moveTo()</code> method, your curve starts at position (0, 0).
*
* If the <code>cubicCurveTo()</code> method succeeds, the Flash runtime sets
* the current drawing position to (<code>anchorX</code>,
* <code>anchorY</code>). If the <code>cubicCurveTo()</code> 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 <code>cubicCurveTo()</code> 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
* (<code>controlX</code>, <code>controlY</code>) specifies. The current
* drawing position is then set to(<code>anchorX</code>,
* <code>anchorY</code>). If the movie clip in which you are drawing contains
* content created with the Flash drawing tools, calls to the
* <code>curveTo()</code> method are drawn underneath this content. If you
* call the <code>curveTo()</code> method before any calls to the
* <code>moveTo()</code> 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.
*
* <p>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. </p>
*
* @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
* <code>drawCircle()</code> method, by calling the <code>linestyle()</code>,
* <code>lineGradientStyle()</code>, <code>beginFill()</code>,
* <code>beginGradientFill()</code>, or <code>beginBitmapFill()</code>
* method.
*
* @param x The <i>x</i> location of the center of the circle relative
* to the registration point of the parent display object(in
* pixels).
* @param y The <i>y</i> 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 -= (<GraphicsStrokeStyle<any>> 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
* <code>drawEllipse()</code> method, by calling the
* <code>linestyle()</code>, <code>lineGradientStyle()</code>,
* <code>beginFill()</code>, <code>beginGradientFill()</code>, or
* <code>beginBitmapFill()</code> method.
*
* @param x The <i>x</i> 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 <i>y</i> 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 -= (<GraphicsStrokeStyle<any>> this._active_stroke_path.style).thickness / 2;
h -= (<GraphicsStrokeStyle<any>> 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.
*
* <p> Graphics paths can contain other graphics paths. If the
* <code>graphicsData</code> Vector includes a path, that path and all its
* sub-paths are rendered during this operation. </p>
*
*/
public drawGraphicsData(graphicsData: Array<IGraphicsData>): void {
/*
for (var i:number=0; i<graphicsData.length; i++){
//todo
if(graphicsData[i].dataType=="beginFill"){
}
else if(graphicsData[i].dataType=="endFill"){
}
else if(graphicsData[i].dataType=="endFill"){
}
else if(graphicsData[i].dataType=="Path"){
}
}
*/
}
/**
* Submits a series of commands for drawing. The <code>drawPath()</code>
* method uses vector arrays to consolidate individual <code>moveTo()</code>,
* <code>lineTo()</code>, and <code>curveTo()</code> drawing commands into a
* single call. The <code>drawPath()</code> 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.
*
* <p> Generally, drawings render faster with <code>drawPath()</code> than
* with a series of individual <code>lineTo()</code> and
* <code>curveTo()</code> methods. </p>
*
* <p> The <code>drawPath()</code> 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 <code>drawPath()</code>
* method can have small sub-pixel alignment errors when used in conjunction
* with the <code>lineTo()</code> and <code>curveTo()</code> methods. </p>
*
* <p> The <code>drawPath()</code> method also uses slightly different rules
* for filling and drawing lines. They are: </p>
*
* <ul>
* <li>When a fill is applied to rendering a path:
* <ul>
* <li>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.)</li>
* <li>A sub-path that isn't closed(the end point is not equal to the
* begin point) is implicitly closed.</li>
* </ul>
* </li>
* <li>When a stroke is applied to rendering a path:
* <ul>
* <li>The sub-paths can be composed of any number of points.</li>
* <li>The sub-path is never implicitly closed.</li>
* </ul>
* </li>
* </ul>
*
* @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
* <code>drawRect()</code> method, by calling the <code>linestyle()</code>,
* <code>lineGradientStyle()</code>, <code>beginFill()</code>,
* <code>beginGradientFill()</code>, or <code>beginBitmapFill()</code>
* 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 <code>width</code> or <code>height</code>
* parameters are not a number
* (<code>Number.NaN</code>).
*/
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 = (<GraphicsStrokeStyle<any>> this._active_stroke_path.style).thickness / 2;
w -= (<GraphicsStrokeStyle<any>> this._active_stroke_path.style).thickness;
h -= (<GraphicsStrokeStyle<any>> 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=(<GraphicsStrokeStyle>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 <code>drawRoundRect()</code> method, by calling the
* <code>linestyle()</code>, <code>lineGradientStyle()</code>,
* <code>beginFill()</code>, <code>beginGradientFill()</code>, or
* <code>beginBitmapFill()</code> 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 <code>ellipseWidth</code> parameter.
* @throws ArgumentError If the <code>width</code>, <code>height</code>,
* <code>ellipseWidth</code> or
* <code>ellipseHeight</code> parameters are not a
* number(<code>Number.NaN</code>).
*/
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 = (<GraphicsStrokeStyle<any>> this._active_stroke_path.style).thickness / 2;
w -= (<GraphicsStrokeStyle<any>> this._active_stroke_path.style).thickness;
h -= (<GraphicsStrokeStyle<any>> 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 = (<GraphicsStrokeStyle<any>> this._active_stroke_path.style).thickness / 2;
w -= (<GraphicsStrokeStyle<any>> this._active_stroke_path.style).thickness;
h -= (<GraphicsStrokeStyle<any>> 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 <code>drawTriangles()</code> method maps
* either the current fill, or a bitmap fill, to the triangle faces using a
* set of(u,v) coordinates.
*
* <p> Any type of fill can be used, but if the fill has a transform matrix
* that transform matrix is ignored. </p>
*
* <p> A <code>uvtData</code> parameter improves texture mapping when a
* bitmap fill is used. </p>
*
* @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 drawTriangle