This commit is contained in:
2025-01-04 00:34:03 +01:00
parent 41829408dc
commit 0ca14bbc19
18111 changed files with 1871397 additions and 0 deletions

View File

@@ -0,0 +1,588 @@
import { SmoothGraphicsData } from './core/SmoothGraphicsData.mjs';
import { FILL_COMMANDS } from './shapes/index.mjs';
import { Point, Geometry, Buffer, TYPES, Texture, SHAPES, WRAP_MODES, Matrix, Color } from '@pixi/core';
import { Bounds } from '@pixi/display';
import { BuildData } from './core/BuildData.mjs';
import { SegmentPacker } from './core/SegmentPacker.mjs';
import { BatchPart } from './core/BatchPart.mjs';
import { matrixEquals, BatchDrawCall } from './core/BatchDrawCall.mjs';
const BATCH_POOL = [];
const DRAW_CALL_POOL = [];
const tmpPoint = new Point();
const tmpBounds = new Bounds();
class SmoothGraphicsGeometry extends Geometry {
constructor() {
super();
this.indicesUint16 = null;
this.initAttributes(false);
this.buildData = new BuildData();
this.graphicsData = [];
this.dirty = 0;
this.batchDirty = -1;
this.cacheDirty = -1;
this.clearDirty = 0;
this.drawCalls = [];
this.batches = [];
this.shapeBuildIndex = 0;
this.shapeBatchIndex = 0;
this._bounds = new Bounds();
this.boundsDirty = -1;
this.boundsPadding = 0;
this.batchable = false;
this.indicesUint16 = null;
this.packer = null;
this.packSize = 0;
this.pack32index = null;
}
get points() {
return this.buildData.verts;
}
get closePointEps() {
return this.buildData.closePointEps;
}
initAttributes(_static) {
this._buffer = new Buffer(null, _static, false);
this._bufferFloats = new Float32Array();
this._bufferUint = new Uint32Array();
this._indexBuffer = new Buffer(null, _static, true);
this.addAttribute("aPrev", this._buffer, 2, false, TYPES.FLOAT).addAttribute("aPoint1", this._buffer, 2, false, TYPES.FLOAT).addAttribute("aPoint2", this._buffer, 2, false, TYPES.FLOAT).addAttribute("aNext", this._buffer, 2, false, TYPES.FLOAT).addAttribute("aTravel", this._buffer, 1, false, TYPES.FLOAT).addAttribute("aVertexJoint", this._buffer, 1, false, TYPES.FLOAT).addAttribute("aStyleId", this._buffer, 1, false, TYPES.FLOAT).addAttribute("aColor", this._buffer, 4, true, TYPES.UNSIGNED_BYTE).addIndex(this._indexBuffer);
this.strideFloats = 12;
}
checkInstancing(instanced, allow32Indices) {
if (this.packer) {
return;
}
this.packer = new SegmentPacker();
this.pack32index = allow32Indices;
}
/**
* Get the current bounds of the graphic geometry.
*
* @member {PIXI.Bounds}
* @readonly
*/
get bounds() {
if (this.boundsDirty !== this.dirty) {
this.boundsDirty = this.dirty;
this.calculateBounds();
}
return this._bounds;
}
/**
* Call if you changed graphicsData manually.
* Empties all batch buffers.
*/
invalidate() {
this.boundsDirty = -1;
this.dirty++;
this.batchDirty++;
this.shapeBuildIndex = 0;
this.shapeBatchIndex = 0;
this.packSize = 0;
this.buildData.clear();
for (let i = 0; i < this.drawCalls.length; i++) {
this.drawCalls[i].clear();
DRAW_CALL_POOL.push(this.drawCalls[i]);
}
this.drawCalls.length = 0;
for (let i = 0; i < this.batches.length; i++) {
const batchPart = this.batches[i];
batchPart.reset();
BATCH_POOL.push(batchPart);
}
this.batches.length = 0;
}
clear() {
if (this.graphicsData.length > 0) {
this.invalidate();
this.clearDirty++;
this.graphicsData.length = 0;
}
return this;
}
drawShape(shape, fillStyle = null, lineStyle = null, matrix = null) {
const data = new SmoothGraphicsData(shape, fillStyle, lineStyle, matrix);
this.graphicsData.push(data);
this.dirty++;
return this;
}
drawHole(shape, matrix = null) {
if (!this.graphicsData.length) {
return null;
}
const data = new SmoothGraphicsData(shape, null, null, matrix);
const lastShape = this.graphicsData[this.graphicsData.length - 1];
data.lineStyle = lastShape.lineStyle;
lastShape.holes.push(data);
this.dirty++;
return this;
}
destroy() {
super.destroy();
for (let i = 0; i < this.graphicsData.length; ++i) {
this.graphicsData[i].destroy();
}
this.buildData.destroy();
this.buildData = null;
this.indexBuffer.destroy();
this.indexBuffer = null;
this.graphicsData.length = 0;
this.graphicsData = null;
this.drawCalls.length = 0;
this.drawCalls = null;
this.batches.length = 0;
this.batches = null;
this._bounds = null;
}
/**
* Check to see if a point is contained within this geometry.
*
* @param {PIXI.IPointData} point - Point to check if it's contained.
* @return {Boolean} `true` if the point is contained within geometry.
*/
containsPoint(point) {
const graphicsData = this.graphicsData;
for (let i = 0; i < graphicsData.length; ++i) {
const data = graphicsData[i];
if (!data.fillStyle.visible) {
continue;
}
if (data.shape) {
if (data.matrix) {
data.matrix.applyInverse(point, tmpPoint);
} else {
tmpPoint.copyFrom(point);
}
if (data.shape.contains(tmpPoint.x, tmpPoint.y)) {
let hitHole = false;
if (data.holes) {
for (let i2 = 0; i2 < data.holes.length; i2++) {
const hole = data.holes[i2];
if (hole.shape.contains(tmpPoint.x, tmpPoint.y)) {
hitHole = true;
break;
}
}
}
if (!hitHole) {
return true;
}
}
}
}
return false;
}
updatePoints() {
}
updateBufferSize() {
this._buffer.update(new Float32Array());
}
updateBuild() {
const { graphicsData, buildData } = this;
const len = graphicsData.length;
for (let i = this.shapeBuildIndex; i < len; i++) {
const data = graphicsData[i];
data.strokeStart = 0;
data.strokeLen = 0;
data.fillStart = 0;
data.fillLen = 0;
const { fillStyle, lineStyle, holes } = data;
if (!fillStyle.visible && !lineStyle.visible) {
continue;
}
const command = FILL_COMMANDS[data.type];
data.clearPath();
command.path(data, buildData);
if (data.matrix) {
this.transformPoints(data.points, data.matrix);
}
data.clearBuild();
if (data.points.length <= 2) {
continue;
}
if (fillStyle.visible || lineStyle.visible) {
this.processHoles(holes);
}
if (fillStyle.visible) {
data.fillAA = data.fillStyle.smooth && data.fillStyle.texture === Texture.WHITE && holes.length === 0 && !(data.closeStroke && data.lineStyle.visible && !data.lineStyle.shader && data.lineStyle.alpha >= 0.99 && data.lineStyle.width * Math.min(data.lineStyle.alignment, 1 - data.lineStyle.alignment) >= 0.495);
data.fillStart = buildData.joints.length;
if (holes.length) {
FILL_COMMANDS[SHAPES.POLY].fill(data, buildData);
} else {
command.fill(data, buildData);
}
data.fillLen = buildData.joints.length - data.fillStart;
}
if (lineStyle.visible) {
data.strokeStart = buildData.joints.length;
command.line(data, buildData);
for (let i2 = 0; i2 < holes.length; i2++) {
const hole = holes[i2];
FILL_COMMANDS[hole.type].line(hole, buildData);
}
data.strokeLen = buildData.joints.length - data.strokeStart;
}
}
this.shapeBuildIndex = len;
}
updateBatches(shaderSettings) {
if (!this.graphicsData.length) {
this.batchable = true;
return;
}
this.updateBuild();
if (!this.validateBatching()) {
return;
}
const { buildData, graphicsData } = this;
const len = graphicsData.length;
this.cacheDirty = this.dirty;
let batchPart = null;
let currentStyle = null;
if (this.batches.length > 0) {
batchPart = this.batches[this.batches.length - 1];
currentStyle = batchPart.style;
}
for (let i = this.shapeBatchIndex; i < len; i++) {
const data = graphicsData[i];
const fillStyle = data.fillStyle;
const lineStyle = data.lineStyle;
if (data.matrix) {
this.transformPoints(data.points, data.matrix);
}
if (!fillStyle.visible && !lineStyle.visible) {
continue;
}
for (let j = 0; j < 2; j++) {
const style = j === 0 ? fillStyle : lineStyle;
if (!style.visible)
continue;
const nextTexture = style.texture.baseTexture;
const attribOld = buildData.vertexSize;
const indexOld = buildData.indexSize;
nextTexture.wrapMode = WRAP_MODES.REPEAT;
if (j === 0) {
this.packer.updateBufferSize(data.fillStart, data.fillLen, data.triangles.length, buildData);
} else {
this.packer.updateBufferSize(data.strokeStart, data.strokeLen, data.triangles.length, buildData);
}
const attribSize = buildData.vertexSize;
if (attribSize === attribOld)
continue;
if (batchPart && !this._compareStyles(currentStyle, style)) {
batchPart.end(indexOld, attribOld);
batchPart = null;
}
if (!batchPart) {
batchPart = BATCH_POOL.pop() || new BatchPart();
batchPart.begin(style, indexOld, attribOld);
this.batches.push(batchPart);
currentStyle = style;
}
if (j === 0) {
batchPart.jointEnd = data.fillStart + data.fillLen;
} else {
batchPart.jointEnd = data.strokeStart + data.strokeLen;
}
}
}
this.shapeBatchIndex = len;
if (batchPart) {
batchPart.end(buildData.indexSize, buildData.vertexSize);
}
if (this.batches.length === 0) {
this.batchable = true;
return;
}
this.batchable = this.isBatchable();
if (this.batchable) {
this.packBatches();
} else {
this.buildDrawCalls(shaderSettings);
this.updatePack();
}
}
updatePack() {
const { vertexSize, indexSize } = this.buildData;
if (this.packSize === vertexSize) {
return;
}
const { strideFloats, packer, buildData, batches } = this;
const buffer = this._buffer;
const index = this._indexBuffer;
const floatsSize = vertexSize * strideFloats;
if (buffer.data.length !== floatsSize) {
const arrBuf = new ArrayBuffer(floatsSize * 4);
this._bufferFloats = new Float32Array(arrBuf);
this._bufferUint = new Uint32Array(arrBuf);
buffer.data = this._bufferFloats;
}
if (index.data.length !== indexSize) {
if (vertexSize > 65535 && this.pack32index) {
index.data = new Uint32Array(indexSize);
} else {
index.data = new Uint16Array(indexSize);
}
}
packer.beginPack(buildData, this._bufferFloats, this._bufferUint, index.data);
let j = 0;
for (let i = 0; i < this.graphicsData.length; i++) {
const data = this.graphicsData[i];
if (data.fillLen) {
while (batches[j].jointEnd <= data.fillStart) {
j++;
}
packer.packInterleavedGeometry(
data.fillStart,
data.fillLen,
data.triangles,
batches[j].styleId,
batches[j].rgba
);
}
if (data.strokeLen) {
while (batches[j].jointEnd <= data.strokeStart) {
j++;
}
packer.packInterleavedGeometry(
data.strokeStart,
data.strokeLen,
data.triangles,
batches[j].styleId,
batches[j].rgba
);
}
}
buffer.update();
index.update();
this.packSize = vertexSize;
}
/**
* Affinity check
*
* @param {PIXI.FillStyle | PIXI.LineStyle} styleA
* @param {PIXI.FillStyle | PIXI.LineStyle} styleB
*/
_compareStyles(styleA, styleB) {
if (!styleA || !styleB) {
return false;
}
if (styleA.texture.baseTexture !== styleB.texture.baseTexture) {
return false;
}
if (styleA.color + styleA.alpha !== styleB.color + styleB.alpha) {
return false;
}
if (styleA.shader !== styleB.shader) {
return false;
}
if (styleA.width !== styleB.width) {
return false;
}
if (styleA.scaleMode !== styleB.scaleMode) {
return false;
}
if (styleA.alignment !== styleB.alignment) {
return false;
}
const mat1 = styleA.matrix || Matrix.IDENTITY;
const mat2 = styleB.matrix || Matrix.IDENTITY;
return matrixEquals(mat1, mat2);
}
/**
* Test geometry for batching process.
*
* @protected
*/
validateBatching() {
if (this.dirty === this.cacheDirty || !this.graphicsData.length) {
return false;
}
for (let i = 0, l = this.graphicsData.length; i < l; i++) {
const data = this.graphicsData[i];
const fill = data.fillStyle;
const line = data.lineStyle;
if (fill && !fill.texture.baseTexture.valid)
return false;
if (line && !line.texture.baseTexture.valid)
return false;
}
return true;
}
/**
* Offset the indices so that it works with the batcher.
*
* @protected
*/
packBatches() {
this.batchDirty++;
const batches = this.batches;
for (let i = 0, l = batches.length; i < l; i++) {
const batch = batches[i];
for (let j = 0; j < batch.size; j++) {
const index = batch.start + j;
this.indicesUint16[index] = this.indicesUint16[index] - batch.attribStart;
}
}
}
isBatchable() {
return false;
}
/**
* Converts intermediate batches data to drawCalls.
*
* @protected
*/
buildDrawCalls(shaderSettings) {
for (let i = 0; i < this.drawCalls.length; i++) {
this.drawCalls[i].clear();
DRAW_CALL_POOL.push(this.drawCalls[i]);
}
this.drawCalls.length = 0;
let currentGroup = DRAW_CALL_POOL.pop() || new BatchDrawCall();
currentGroup.begin(shaderSettings, null);
let index = 0;
this.drawCalls.push(currentGroup);
for (let i = 0; i < this.batches.length; i++) {
const batchData = this.batches[i];
const style = batchData.style;
if (batchData.attribSize === 0) {
continue;
}
let styleId = -1;
const mat = style.getTextureMatrix();
if (currentGroup.check(style.shader)) {
styleId = currentGroup.add(
style.texture,
mat,
style.width,
style.alignment || 0,
style.packLineScale()
);
}
if (styleId < 0) {
currentGroup = DRAW_CALL_POOL.pop() || new BatchDrawCall();
this.drawCalls.push(currentGroup);
currentGroup.begin(shaderSettings, style.shader);
currentGroup.start = index;
styleId = currentGroup.add(
style.texture,
mat,
style.width,
style.alignment || 0,
style.packLineScale()
);
}
currentGroup.size += batchData.size;
index += batchData.size;
const { color, alpha } = style;
const bgr = Color.shared.setValue(color).toLittleEndianNumber();
batchData.rgba = Color.shared.setValue(bgr).toPremultiplied(alpha);
batchData.styleId = styleId;
}
}
processHoles(holes) {
for (let i = 0; i < holes.length; i++) {
const hole = holes[i];
const command = FILL_COMMANDS[hole.type];
hole.clearPath();
command.path(hole, this.buildData);
if (hole.matrix) {
this.transformPoints(hole.points, hole.matrix);
}
}
}
/**
* Update the local bounds of the object. Expensive to use performance-wise.
*
* @protected
*/
calculateBounds() {
const bounds = this._bounds;
const sequenceBounds = tmpBounds;
let curMatrix = Matrix.IDENTITY;
this._bounds.clear();
sequenceBounds.clear();
for (let i = 0; i < this.graphicsData.length; i++) {
const data = this.graphicsData[i];
const shape = data.shape;
const type = data.type;
const lineStyle = data.lineStyle;
const nextMatrix = data.matrix || Matrix.IDENTITY;
let lineWidth = 0;
if (lineStyle && lineStyle.visible) {
lineWidth = lineStyle.width;
if (type !== SHAPES.POLY || data.fillStyle.visible) {
lineWidth *= Math.max(0, lineStyle.alignment);
} else {
lineWidth *= Math.max(lineStyle.alignment, 1 - lineStyle.alignment);
}
}
if (curMatrix !== nextMatrix) {
if (!sequenceBounds.isEmpty()) {
bounds.addBoundsMatrix(sequenceBounds, curMatrix);
sequenceBounds.clear();
}
curMatrix = nextMatrix;
}
if (type === SHAPES.RECT || type === SHAPES.RREC) {
const rect = shape;
sequenceBounds.addFramePad(
rect.x,
rect.y,
rect.x + rect.width,
rect.y + rect.height,
lineWidth,
lineWidth
);
} else if (type === SHAPES.CIRC) {
const circle = shape;
sequenceBounds.addFramePad(
circle.x,
circle.y,
circle.x,
circle.y,
circle.radius + lineWidth,
circle.radius + lineWidth
);
} else if (type === SHAPES.ELIP) {
const ellipse = shape;
sequenceBounds.addFramePad(
ellipse.x,
ellipse.y,
ellipse.x,
ellipse.y,
ellipse.width + lineWidth,
ellipse.height + lineWidth
);
} else {
const poly = shape;
bounds.addVerticesMatrix(curMatrix, poly.points, 0, poly.points.length, lineWidth, lineWidth);
}
}
if (!sequenceBounds.isEmpty()) {
bounds.addBoundsMatrix(sequenceBounds, curMatrix);
}
bounds.pad(this.boundsPadding, this.boundsPadding);
}
/**
* Transform points using matrix.
*
* @protected
* @param {number[]} points - Points to transform
* @param {PIXI.Matrix} matrix - Transform matrix
*/
transformPoints(points, matrix) {
for (let i = 0; i < points.length / 2; i++) {
const x = points[i * 2];
const y = points[i * 2 + 1];
points[i * 2] = matrix.a * x + matrix.c * y + matrix.tx;
points[i * 2 + 1] = matrix.b * x + matrix.d * y + matrix.ty;
}
}
}
SmoothGraphicsGeometry.BATCHABLE_SIZE = 100;
export { BATCH_POOL, DRAW_CALL_POOL, SmoothGraphicsGeometry };
//# sourceMappingURL=SmoothGraphicsGeometry.mjs.map