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