@esotericsoftware/spine-core
Version:
The official Spine Runtimes for the web.
804 lines • 138 kB
JavaScript
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated April 5, 2025. Replaces all prior versions.
*
* Copyright (c) 2013-2025, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { Triangulator } from "./Triangulator.js";
import { Utils } from "./Utils.js";
export class SkeletonClipping {
triangulator = null;
clippingPolygon = [];
clippingPolygons = [];
clipOutput = [];
clippedVertices = [];
/** An empty array unless {@link clipTrianglesUnpacked} was used. **/
clippedUVs = [];
clippedTriangles = [];
inverseVertices = [];
_clippedVerticesTyped = new Float32Array(1024);
_clippedUVsTyped = new Float32Array(1024);
_clippedTrianglesTyped = new Uint16Array(1024);
clippedVerticesTyped = new Float32Array(0);
clippedUVsTyped = new Float32Array(0);
clippedTrianglesTyped = new Uint16Array(0);
clippedVerticesLength = 0;
clippedUVsLength = 0;
clippedTrianglesLength = 0;
scratch = [];
inverse = false;
clipAttachment = null;
clipStart(skeleton, slot, clip) {
if (this.clipAttachment)
return;
const n = clip.worldVerticesLength;
this.clipAttachment = clip;
this.inverse = clip.inverse;
const vertices = Utils.setArraySize(this.clippingPolygon, n);
clip.computeWorldVertices(skeleton, slot, 0, n, vertices, 0, 2);
const clippingPolygon = this.clippingPolygon;
const convex = this.makeClockwise(clippingPolygon);
if (convex || this.inverse || clip.convex) {
if (!convex)
this.makeConvex(clippingPolygon);
this.clippingPolygon.push(clippingPolygon[0], clippingPolygon[1]);
this.clippingPolygons.push(clippingPolygon);
}
else {
if (this.triangulator === null)
this.triangulator = new Triangulator();
this.clippingPolygons.push(...this.triangulator.decompose(clippingPolygon, this.triangulator.triangulate(clippingPolygon)));
}
}
clipEnd(slot) {
if (!this.clipAttachment)
return;
if (slot && this.clipAttachment.endSlot !== slot.data)
return;
this.clipAttachment = null;
this.clippingPolygons.length = 0;
}
isClipping() {
return this.clipAttachment != null;
}
clipTriangles(vertices, triangles, trianglesLength, uvs, light, dark, twoColor, stride) {
return (uvs && light && dark && typeof twoColor === 'boolean' && typeof stride === 'number')
? this.clipTrianglesRender(vertices, triangles, trianglesLength, uvs, light, dark, twoColor, stride)
: this.clipTrianglesNoRender(vertices, triangles, trianglesLength);
}
clipTrianglesNoRender(vertices, triangles, trianglesLength) {
const clippedVertices = this.clippedVertices;
clippedVertices.length = 0;
const clippedTriangles = this.clippedTriangles;
clippedTriangles.length = 0;
let index = 0;
if (this.inverse) {
const polygon = this.clippingPolygons[0];
for (let i = 0; i < trianglesLength; i += 3) {
let t = triangles[i] << 1;
const x1 = vertices[t], y1 = vertices[t + 1];
t = triangles[i + 1] << 1;
const x2 = vertices[t], y2 = vertices[t + 1];
t = triangles[i + 2] << 1;
const x3 = vertices[t], y3 = vertices[t + 1];
this.clipInverse(x1, y1, x2, y2, x3, y3, polygon);
const iv = this.inverseVertices;
for (let offset = 0, nn = this.inverseVertices.length; offset < nn;) {
const polygonSize = iv[offset++];
let vertexCount = polygonSize >> 1, s = clippedVertices.length;
const cv = Utils.setArraySize(clippedVertices, s + polygonSize);
Utils.arrayCopy(iv, offset, cv, s, polygonSize);
s = clippedTriangles.length;
const ct = Utils.setArraySize(clippedTriangles, s + 3 * (vertexCount - 2));
for (let ii = 1; ii < vertexCount - 1; ii++, s += 3) {
ct[s] = index;
ct[s + 1] = index + ii;
ct[s + 2] = index + ii + 1;
}
index += vertexCount;
offset += polygonSize;
}
}
return true;
}
const clipOutput = this.clipOutput;
const polygons = this.clippingPolygons;
const polygonsCount = polygons.length;
let clipOutputItems = null;
for (let i = 0; i < trianglesLength; i += 3) {
let t = triangles[i] << 1;
const x1 = vertices[t], y1 = vertices[t + 1];
t = triangles[i + 1] << 1;
const x2 = vertices[t], y2 = vertices[t + 1];
t = triangles[i + 2] << 1;
const x3 = vertices[t], y3 = vertices[t + 1];
for (let p = 0; p < polygonsCount; p++) {
let s = clippedVertices.length;
if (this.clip(x1, y1, x2, y2, x3, y3, polygons[p])) {
clipOutputItems = this.clipOutput;
const clipOutputLength = clipOutput.length;
if (clipOutputLength === 0)
continue;
let clipOutputCount = clipOutputLength >> 1;
const cv = Utils.setArraySize(clippedVertices, s + clipOutputLength);
Utils.arrayCopy(clipOutputItems, 0, cv, s, clipOutputLength);
s = clippedTriangles.length;
const ct = Utils.setArraySize(clippedTriangles, s + 3 * (clipOutputCount - 2));
clipOutputCount--;
for (let ii = 1; ii < clipOutputCount; ii++, s += 3) {
ct[s] = index;
ct[s + 1] = (index + ii);
ct[s + 2] = (index + ii + 1);
}
index += clipOutputCount;
}
else {
const cv = Utils.setArraySize(clippedVertices, s + 3 * 2);
cv[s] = x1;
cv[s + 1] = y1;
cv[s + 2] = x2;
cv[s + 3] = y2;
cv[s + 4] = x3;
cv[s + 5] = y3;
s = clippedTriangles.length;
const ct = Utils.setArraySize(clippedTriangles, s + 3);
ct[s] = index;
ct[s + 1] = (index + 1);
ct[s + 2] = (index + 2);
index += 3;
break;
}
}
}
return clipOutputItems != null;
}
clipTrianglesRender(vertices, triangles, trianglesLength, uvs, light, dark, twoColor, stride) {
const clippedVertices = this.clippedVertices;
clippedVertices.length = 0;
const clippedTriangles = this.clippedTriangles;
clippedTriangles.length = 0;
let index = 0;
if (this.inverse) {
const polygon = this.clippingPolygons[0];
for (let i = 0; i < trianglesLength; i += 3) {
let t0 = triangles[i], t1 = triangles[i + 1], t2 = triangles[i + 2];
const x1 = vertices[t0 * stride], y1 = vertices[t0 * stride + 1];
const x2 = vertices[t1 * stride], y2 = vertices[t1 * stride + 1];
const x3 = vertices[t2 * stride], y3 = vertices[t2 * stride + 1];
this.clipInverse(x1, y1, x2, y2, x3, y3, polygon);
const nn = this.inverseVertices.length;
if (nn === 0)
continue;
const u1 = uvs[t0 <<= 1], v1 = uvs[t0 + 1];
const u2 = uvs[t1 <<= 1], v2 = uvs[t1 + 1];
const u3 = uvs[t2 <<= 1], v3 = uvs[t2 + 1];
const d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1, d = 1 / (d0 * d2 + d1 * (y1 - y3));
const iv = this.inverseVertices;
for (let offset = 0; offset < nn;) {
const polygonSize = iv[offset++];
const vertexCount = polygonSize >> 1;
let s = clippedVertices.length;
const cv = Utils.setArraySize(clippedVertices, s + vertexCount * stride);
for (let ii = 0; ii < polygonSize; ii += 2, s += stride) {
const x = iv[offset + ii], y = iv[offset + ii + 1];
cv[s] = x;
cv[s + 1] = y;
cv[s + 2] = light.r;
cv[s + 3] = light.g;
cv[s + 4] = light.b;
cv[s + 5] = light.a;
const c0 = x - x3, c1 = y - y3, a = (d0 * c0 + d1 * c1) * d, b = (d4 * c0 + d2 * c1) * d, c = 1 - a - b;
cv[s + 6] = u1 * a + u2 * b + u3 * c;
cv[s + 7] = v1 * a + v2 * b + v3 * c;
if (twoColor) {
cv[s + 8] = dark.r;
cv[s + 9] = dark.g;
cv[s + 10] = dark.b;
cv[s + 11] = dark.a;
}
}
s = clippedTriangles.length;
const ct = Utils.setArraySize(clippedTriangles, s + 3 * (vertexCount - 2));
for (let ii = 1; ii < vertexCount - 1; ii++, s += 3) {
ct[s] = index;
ct[s + 1] = index + ii;
ct[s + 2] = index + ii + 1;
}
index += vertexCount;
offset += polygonSize;
}
}
return true;
}
const clipOutput = this.clipOutput;
const polygons = this.clippingPolygons;
const polygonsCount = this.clippingPolygons.length;
let clipOutputItems = null;
for (let i = 0; i < trianglesLength; i += 3) {
let t = triangles[i];
const x1 = vertices[t * stride], y1 = vertices[t * stride + 1];
const u1 = uvs[t << 1], v1 = uvs[(t << 1) + 1];
t = triangles[i + 1];
const x2 = vertices[t * stride], y2 = vertices[t * stride + 1];
const u2 = uvs[t << 1], v2 = uvs[(t << 1) + 1];
t = triangles[i + 2];
const x3 = vertices[t * stride], y3 = vertices[t * stride + 1];
const u3 = uvs[t << 1], v3 = uvs[(t << 1) + 1];
const d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1, d = 1 / (d0 * d2 + d1 * (y1 - y3));
for (let p = 0; p < polygonsCount; p++) {
let s = clippedVertices.length;
if (this.clip(x1, y1, x2, y2, x3, y3, polygons[p])) {
clipOutputItems = this.clipOutput;
const clipOutputLength = clipOutput.length;
if (clipOutputLength === 0)
continue;
let clipOutputCount = clipOutputLength >> 1;
const cv = Utils.setArraySize(clippedVertices, s + clipOutputCount * stride);
for (let ii = 0; ii < clipOutputLength; ii += 2, s += stride) {
const x = clipOutputItems[ii], y = clipOutputItems[ii + 1];
cv[s] = x;
cv[s + 1] = y;
cv[s + 2] = light.r;
cv[s + 3] = light.g;
cv[s + 4] = light.b;
cv[s + 5] = light.a;
const c0 = x - x3, c1 = y - y3, a = (d0 * c0 + d1 * c1) * d, b = (d4 * c0 + d2 * c1) * d, c = 1 - a - b;
cv[s + 6] = u1 * a + u2 * b + u3 * c;
cv[s + 7] = v1 * a + v2 * b + v3 * c;
if (twoColor) {
cv[s + 8] = dark.r;
cv[s + 9] = dark.g;
cv[s + 10] = dark.b;
cv[s + 11] = dark.a;
}
}
s = clippedTriangles.length;
const ct = Utils.setArraySize(clippedTriangles, s + 3 * (clipOutputCount - 2));
clipOutputCount--;
for (let ii = 1; ii < clipOutputCount; ii++, s += 3) {
ct[s] = index;
ct[s + 1] = (index + ii);
ct[s + 2] = (index + ii + 1);
}
index += clipOutputCount + 1;
}
else {
const cv = Utils.setArraySize(clippedVertices, s + 3 * stride);
cv[s] = x1;
cv[s + 1] = y1;
cv[s + 2] = light.r;
cv[s + 3] = light.g;
cv[s + 4] = light.b;
cv[s + 5] = light.a;
if (!twoColor) {
cv[s + 6] = u1;
cv[s + 7] = v1;
cv[s + 8] = x2;
cv[s + 9] = y2;
cv[s + 10] = light.r;
cv[s + 11] = light.g;
cv[s + 12] = light.b;
cv[s + 13] = light.a;
cv[s + 14] = u2;
cv[s + 15] = v2;
cv[s + 16] = x3;
cv[s + 17] = y3;
cv[s + 18] = light.r;
cv[s + 19] = light.g;
cv[s + 20] = light.b;
cv[s + 21] = light.a;
cv[s + 22] = u3;
cv[s + 23] = v3;
}
else {
cv[s + 6] = u1;
cv[s + 7] = v1;
cv[s + 8] = dark.r;
cv[s + 9] = dark.g;
cv[s + 10] = dark.b;
cv[s + 11] = dark.a;
cv[s + 12] = x2;
cv[s + 13] = y2;
cv[s + 14] = light.r;
cv[s + 15] = light.g;
cv[s + 16] = light.b;
cv[s + 17] = light.a;
cv[s + 18] = u2;
cv[s + 19] = v2;
cv[s + 20] = dark.r;
cv[s + 21] = dark.g;
cv[s + 22] = dark.b;
cv[s + 23] = dark.a;
cv[s + 24] = x3;
cv[s + 25] = y3;
cv[s + 26] = light.r;
cv[s + 27] = light.g;
cv[s + 28] = light.b;
cv[s + 29] = light.a;
cv[s + 30] = u3;
cv[s + 31] = v3;
cv[s + 32] = dark.r;
cv[s + 33] = dark.g;
cv[s + 34] = dark.b;
cv[s + 35] = dark.a;
}
s = clippedTriangles.length;
const ct = Utils.setArraySize(clippedTriangles, s + 3);
ct[s] = index;
ct[s + 1] = (index + 1);
ct[s + 2] = (index + 2);
index += 3;
break;
}
}
}
return clipOutputItems != null;
}
clipTrianglesUnpacked(vertices, vertexStart, triangles, trianglesLength, uvs, stride = 2) {
let clippedVertices = this._clippedVerticesTyped;
let clippedUVs = this._clippedUVsTyped;
let clippedTriangles = this._clippedTrianglesTyped;
let index = 0;
this.clippedVerticesLength = 0;
this.clippedUVsLength = 0;
this.clippedTrianglesLength = 0;
if (this.inverse) {
const polygon = this.clippingPolygons[0];
for (let i = 0; i < trianglesLength; i += 3) {
let v = triangles[i] * stride;
const x1 = vertices[vertexStart + v], y1 = vertices[vertexStart + v + 1];
let uv = triangles[i] << 1;
const u1 = uvs[uv], v1 = uvs[uv + 1];
v = triangles[i + 1] * stride;
const x2 = vertices[vertexStart + v], y2 = vertices[vertexStart + v + 1];
uv = triangles[i + 1] << 1;
const u2 = uvs[uv], v2 = uvs[uv + 1];
v = triangles[i + 2] * stride;
const x3 = vertices[vertexStart + v], y3 = vertices[vertexStart + v + 1];
uv = triangles[i + 2] << 1;
const u3 = uvs[uv], v3 = uvs[uv + 1];
this.clipInverse(x1, y1, x2, y2, x3, y3, polygon);
const nn = this.inverseVertices.length;
if (nn === 0)
continue;
const d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1, d = 1 / (d0 * d2 + d1 * (y1 - y3));
const iv = this.inverseVertices;
for (let offset = 0; offset < nn;) {
const polygonSize = iv[offset++];
const vertexCount = polygonSize >> 1;
let s = this.clippedVerticesLength;
const newLength = s + vertexCount * stride;
const newUVLength = this.clippedUVsLength + vertexCount * 2;
if (clippedVertices.length < newLength) {
this._clippedVerticesTyped = new Float32Array(newLength * 2);
this._clippedVerticesTyped.set(clippedVertices.subarray(0, s));
clippedVertices = this._clippedVerticesTyped;
}
if (clippedUVs.length < newUVLength) {
this._clippedUVsTyped = new Float32Array(newUVLength * 2);
this._clippedUVsTyped.set(clippedUVs.subarray(0, this.clippedUVsLength));
clippedUVs = this._clippedUVsTyped;
}
this.clippedVerticesLength = newLength;
this.clippedUVsLength = newUVLength;
const cv = this._clippedVerticesTyped;
const cu = this._clippedUVsTyped;
let uvIndex = newUVLength - vertexCount * 2;
for (let ii = 0; ii < polygonSize; ii += 2, s += stride, uvIndex += 2) {
const x = iv[offset + ii], y = iv[offset + ii + 1];
cv[s] = x;
cv[s + 1] = y;
const c0 = x - x3, c1 = y - y3, a = (d0 * c0 + d1 * c1) * d, b = (d4 * c0 + d2 * c1) * d, c = 1 - a - b;
cu[uvIndex] = u1 * a + u2 * b + u3 * c;
cu[uvIndex + 1] = v1 * a + v2 * b + v3 * c;
}
s = this.clippedTrianglesLength;
const newLengthTriangles = s + 3 * (vertexCount - 2);
if (clippedTriangles.length < newLengthTriangles) {
this._clippedTrianglesTyped = new Uint16Array(newLengthTriangles * 2);
this._clippedTrianglesTyped.set(clippedTriangles.subarray(0, s));
clippedTriangles = this._clippedTrianglesTyped;
}
this.clippedTrianglesLength = newLengthTriangles;
const ct = clippedTriangles;
for (let ii = 1; ii < vertexCount - 1; ii++, s += 3) {
ct[s] = index;
ct[s + 1] = index + ii;
ct[s + 2] = index + ii + 1;
}
index += vertexCount;
offset += polygonSize;
}
}
this.clippedVerticesTyped = this._clippedVerticesTyped.subarray(0, this.clippedVerticesLength);
this.clippedUVsTyped = this._clippedUVsTyped.subarray(0, this.clippedUVsLength);
this.clippedTrianglesTyped = this._clippedTrianglesTyped.subarray(0, this.clippedTrianglesLength);
return true;
}
const clipOutput = this.clipOutput;
const polygons = this.clippingPolygons;
const polygonsCount = this.clippingPolygons.length;
let clipOutputItems = null;
for (let i = 0; i < trianglesLength; i += 3) {
let t = triangles[i];
let v = t * stride;
const x1 = vertices[vertexStart + v], y1 = vertices[vertexStart + v + 1];
let uv = t << 1;
const u1 = uvs[uv], v1 = uvs[uv + 1];
t = triangles[i + 1];
v = t * stride;
const x2 = vertices[vertexStart + v], y2 = vertices[vertexStart + v + 1];
uv = t << 1;
const u2 = uvs[uv], v2 = uvs[uv + 1];
t = triangles[i + 2];
v = t * stride;
const x3 = vertices[vertexStart + v], y3 = vertices[vertexStart + v + 1];
uv = t << 1;
const u3 = uvs[uv], v3 = uvs[uv + 1];
const d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1, d = 1 / (d0 * d2 + d1 * (y1 - y3));
for (let p = 0; p < polygonsCount; p++) {
let s = this.clippedVerticesLength;
if (this.clip(x1, y1, x2, y2, x3, y3, polygons[p])) {
clipOutputItems = clipOutput;
const clipOutputLength = clipOutput.length;
if (clipOutputLength === 0)
continue;
let clipOutputCount = clipOutputLength >> 1;
const newLength = s + clipOutputCount * stride;
if (clippedVertices.length < newLength) {
this._clippedVerticesTyped = new Float32Array(newLength * 2);
this._clippedVerticesTyped.set(clippedVertices.subarray(0, s));
this._clippedUVsTyped = new Float32Array((this.clippedUVsLength + clipOutputCount * 2) * 2);
this._clippedUVsTyped.set(clippedUVs.subarray(0, this.clippedUVsLength));
clippedVertices = this._clippedVerticesTyped;
clippedUVs = this._clippedUVsTyped;
}
const cv = clippedVertices;
const cu = clippedUVs;
this.clippedVerticesLength = newLength;
let uvIndex = this.clippedUVsLength;
this.clippedUVsLength = uvIndex + clipOutputCount * 2;
for (let ii = 0; ii < clipOutputLength; ii += 2, s += stride, uvIndex += 2) {
const x = clipOutputItems[ii], y = clipOutputItems[ii + 1];
cv[s] = x;
cv[s + 1] = y;
const c0 = x - x3, c1 = y - y3, a = (d0 * c0 + d1 * c1) * d, b = (d4 * c0 + d2 * c1) * d, c = 1 - a - b;
cu[uvIndex] = u1 * a + u2 * b + u3 * c;
cu[uvIndex + 1] = v1 * a + v2 * b + v3 * c;
}
s = this.clippedTrianglesLength;
const newLengthTriangles = s + 3 * (clipOutputCount - 2);
if (clippedTriangles.length < newLengthTriangles) {
this._clippedTrianglesTyped = new Uint16Array(newLengthTriangles * 2);
this._clippedTrianglesTyped.set(clippedTriangles.subarray(0, s));
clippedTriangles = this._clippedTrianglesTyped;
}
this.clippedTrianglesLength = newLengthTriangles;
const ct = clippedTriangles;
clipOutputCount--;
for (let ii = 1; ii < clipOutputCount; ii++, s += 3) {
ct[s] = index;
ct[s + 1] = (index + ii);
ct[s + 2] = (index + ii + 1);
}
index += clipOutputCount + 1;
}
else {
let newLength = s + 3 * stride;
if (clippedVertices.length < newLength) {
this._clippedVerticesTyped = new Float32Array(newLength * 2);
this._clippedVerticesTyped.set(clippedVertices.subarray(0, s));
clippedVertices = this._clippedVerticesTyped;
}
clippedVertices[s] = x1;
clippedVertices[s + 1] = y1;
clippedVertices[s + stride] = x2;
clippedVertices[s + stride + 1] = y2;
clippedVertices[s + stride * 2] = x3;
clippedVertices[s + stride * 2 + 1] = y3;
const uvLength = this.clippedUVsLength + 3 * 2;
if (clippedUVs.length < uvLength) {
this._clippedUVsTyped = new Float32Array(uvLength * 2);
this._clippedUVsTyped.set(clippedUVs.subarray(0, this.clippedUVsLength));
clippedUVs = this._clippedUVsTyped;
}
const uvIndex = this.clippedUVsLength;
clippedUVs[uvIndex] = u1;
clippedUVs[uvIndex + 1] = v1;
clippedUVs[uvIndex + 2] = u2;
clippedUVs[uvIndex + 3] = v2;
clippedUVs[uvIndex + 4] = u3;
clippedUVs[uvIndex + 5] = v3;
this.clippedVerticesLength = newLength;
this.clippedUVsLength = uvLength;
s = this.clippedTrianglesLength;
newLength = s + 3;
if (clippedTriangles.length < newLength) {
this._clippedTrianglesTyped = new Uint16Array(newLength * 2);
this._clippedTrianglesTyped.set(clippedTriangles.subarray(0, s));
clippedTriangles = this._clippedTrianglesTyped;
}
const ct = clippedTriangles;
ct[s] = index;
ct[s + 1] = (index + 1);
ct[s + 2] = (index + 2);
index += 3;
this.clippedTrianglesLength = newLength;
break;
}
}
}
this.clippedVerticesTyped = this._clippedVerticesTyped.subarray(0, this.clippedVerticesLength);
this.clippedUVsTyped = this._clippedUVsTyped.subarray(0, this.clippedUVsLength);
this.clippedTrianglesTyped = this._clippedTrianglesTyped.subarray(0, this.clippedTrianglesLength);
return clipOutputItems !== null;
}
clip(x1, y1, x2, y2, x3, y3, polygon) {
const originalOutput = this.clipOutput;
let clipped = false;
// Avoid copy at the end.
let input, output;
if (polygon.length % 4 >= 2) {
input = this.clipOutput;
output = this.scratch;
}
else {
input = this.scratch;
output = this.clipOutput;
}
const v = polygon;
input.length = 8;
const iv = input;
iv[0] = x1;
iv[1] = y1;
iv[2] = x2;
iv[3] = y2;
iv[4] = x3;
iv[5] = y3;
iv[6] = x1;
iv[7] = y1;
output.length = 0;
const last = polygon.length - 4;
for (let i = 0;; i += 2) {
const edgeX = v[i], edgeY = v[i + 1], ex = edgeX - v[i + 2], ey = edgeY - v[i + 3];
const outputStart = output.length;
const iv = input;
for (let ii = 0, nn = input.length - 2; ii < nn;) {
x1 = iv[ii];
y1 = iv[ii + 1];
ii += 2;
x2 = iv[ii];
y2 = iv[ii + 1];
const s2 = ey * (edgeX - x2) > ex * (edgeY - y2);
const s1 = ey * (edgeX - x1) - ex * (edgeY - y1);
if (s1 > 0) {
if (s2) // v1 in, v2 in
output.push(x2, y2);
else { // v1 in, v2 out
const ix = x2 - x1, iy = y2 - y1, t = s1 / (ix * ey - iy * ex);
if (t >= 0 && t <= 1) {
output.push(x1 + ix * t, y1 + iy * t);
clipped = true;
}
else
output.push(x2, y2);
}
}
else if (s2) { // v1 out, v2 in
const ix = x2 - x1, iy = y2 - y1, t = s1 / (ix * ey - iy * ex);
if (t >= 0 && t <= 1) {
output.push(x1 + ix * t, y1 + iy * t, x2, y2);
clipped = true;
}
else
output.push(x2, y2);
}
else // v1 out, v2 out
clipped = true;
}
if (outputStart === output.length) { // All outside.
originalOutput.length = 0;
return true;
}
output.push(output[0], output[1]);
if (i === last)
break;
const temp = output;
output = input;
output.length = 0;
input = temp;
}
if (originalOutput !== output) {
originalOutput.length = 0;
for (let i = 0, n = output.length - 2; i < n; i++)
originalOutput[i] = output[i];
}
else
originalOutput.length = originalOutput.length - 2;
return clipped;
}
clipInverse(x1, y1, x2, y2, x3, y3, polygon) {
this.inverseVertices.length = 0;
const vLast = polygon.length - 4;
let input, output; // Avoid copy at the end.
if (polygon.length % 4 >= 2) {
input = this.clipOutput;
output = this.scratch;
}
else {
input = this.scratch;
output = this.clipOutput;
}
input.length = 8;
let v = polygon, iv = input;
iv[0] = x1;
iv[1] = y1;
iv[2] = x2;
iv[3] = y2;
iv[4] = x3;
iv[5] = y3;
iv[6] = x1;
iv[7] = y1;
output.length = 0;
for (let i = 0;; i += 2) {
const edgeX = v[i], edgeY = v[i + 1], ex = edgeX - v[i + 2], ey = edgeY - v[i + 3];
const outputStart = output.length, fragmentStart = this.inverseVertices.length;
this.inverseVertices.push(0);
iv = input;
for (let ii = 0, nn = input.length - 2; ii < nn;) {
x1 = iv[ii];
y1 = iv[ii + 1];
ii += 2;
x2 = iv[ii];
y2 = iv[ii + 1];
const s2 = ey * (edgeX - x2) > ex * (edgeY - y2);
const s1 = ey * (edgeX - x1) - ex * (edgeY - y1);
if (s1 > 0) {
if (s2) // v1 in, v2 in
output.push(x2, y2);
else {
// v1 in, v2 out
const ix = x2 - x1, iy = y2 - y1, t = s1 / (ix * ey - iy * ex);
if (t >= 0 && t <= 1) {
const cx = x1 + ix * t, cy = y1 + iy * t;
output.push(cx, cy);
this.inverseVertices.push(cx, cy, x2, y2);
}
else
output.push(x2, y2);
}
}
else if (s2) { // v1 out, v2 in
const dx = x2 - x1, dy = y2 - y1, t = s1 / (dx * ey - dy * ex);
if (t >= 0 && t <= 1) {
const cx = x1 + dx * t, cy = y1 + dy * t;
this.inverseVertices.push(cx, cy);
output.push(cx, cy, x2, y2);
}
else
output.push(x2, y2);
}
else // v1 out, v2 out
this.inverseVertices.push(x2, y2);
}
const fragmentSize = this.inverseVertices.length - fragmentStart - 1;
if (fragmentSize >= 6)
this.inverseVertices[fragmentStart] = fragmentSize;
else
this.inverseVertices.length = fragmentStart; // Degenerate.
if (outputStart === output.length)
break; // All outside.
output.push(output[0], output[1]);
if (i === vLast)
break;
const temp = output;
output = input;
output.length = 0;
input = temp;
}
}
makeClockwise(polygon) {
const v = polygon;
const n = polygon.length;
let noCW = true, noCCW = true;
let area = 0, prevX = v[n - 2], prevY = v[n - 1], currX = v[0], currY = v[1];
for (let i = 2; i < n; i += 2) {
const nextX = v[i], nextY = v[i + 1];
area += currX * nextY - nextX * currY;
const cross = (currX - prevX) * (nextY - currY) - (currY - prevY) * (nextX - currX);
noCCW = noCCW && cross <= 0;
noCW = noCW && cross >= 0;
prevX = currX;
prevY = currY;
currX = nextX;
currY = nextY;
}
area += currX * v[1] - v[0] * currY;
const cross = (currX - prevX) * (v[1] - currY) - (currY - prevY) * (v[0] - currX);
noCCW = noCCW && cross <= 0;
noCW = noCW && cross >= 0;
if (area >= 0) {
for (let i = 0, lastX = n - 2, half = n >> 1; i < half; i += 2) {
const x = v[i], y = v[i + 1];
const other = lastX - i;
v[i] = v[other];
v[i + 1] = v[other + 1];
v[other] = x;
v[other + 1] = y;
}
return noCW;
}
return noCCW;
}
makeConvex(polygon) {
const n = polygon.length;
const v = polygon;
this.clipOutput.length = n;
const sorted = this.clipOutput;
sorted[0] = v[0];
sorted[1] = v[1];
for (let i = 2; i < n; i += 2) {
const x = v[i], y = v[i + 1];
let p = i - 2;
for (; p >= 0 && (sorted[p] > x || (sorted[p] === x && sorted[p + 1] > y)); p -= 2) {
sorted[p + 2] = sorted[p];
sorted[p + 3] = sorted[p + 1];
}
sorted[p + 2] = x;
sorted[p + 3] = y;
}
v[0] = sorted[0];
v[1] = sorted[1];
v[2] = sorted[2];
v[3] = sorted[3];
let s = 4;
for (let i = 4; i < n; i += 2, s += 2) {
const x = sorted[i], y = sorted[i + 1];
while ((v[s - 2] - v[s - 4]) * (y - v[s - 3]) - (v[s - 1] - v[s - 3]) * (x - v[s - 4]) >= 0) {
s -= 2;
if (s === 2)
break;
}
v[s] = x;
v[s + 1] = y;
}
v[s] = sorted[n - 4];
v[s + 1] = sorted[n - 3];
const t = s;
s += 2;
for (let i = n - 6; i >= 0; i -= 2, s += 2) {
const x = sorted[i], y = sorted[i + 1];
while ((v[s - 2] - v[s - 4]) * (y - v[s - 3]) - (v[s - 1] - v[s - 3]) * (x - v[s - 4]) >= 0) {
s -= 2;
if (s === t)
break;
}
v[s] = x;
v[s + 1] = y;
}
polygon.length = s - 2;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2tlbGV0b25DbGlwcGluZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9Ta2VsZXRvbkNsaXBwaW5nLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7K0VBMkIrRTtBQUsvRSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFDakQsT0FBTyxFQUFvQyxLQUFLLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFFckUsTUFBTSxPQUFPLGdCQUFnQjtJQUNwQixZQUFZLEdBQXdCLElBQUksQ0FBQztJQUN6QyxlQUFlLEdBQUcsRUFBYyxDQUFDO0lBQ2pDLGdCQUFnQixHQUFlLEVBQUUsQ0FBQztJQUNsQyxVQUFVLEdBQUcsRUFBYyxDQUFDO0lBQ3BDLGVBQWUsR0FBRyxFQUFjLENBQUM7SUFFakMscUVBQXFFO0lBQ3JFLFVBQVUsR0FBRyxFQUFjLENBQUM7SUFFNUIsZ0JBQWdCLEdBQUcsRUFBYyxDQUFDO0lBQ2xDLGVBQWUsR0FBRyxFQUFjLENBQUM7SUFFakMscUJBQXFCLEdBQWlCLElBQUksWUFBWSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzdELGdCQUFnQixHQUFpQixJQUFJLFlBQVksQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUN4RCxzQkFBc0IsR0FBZ0IsSUFBSSxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDNUQsb0JBQW9CLEdBQWlCLElBQUksWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3pELGVBQWUsR0FBaUIsSUFBSSxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDcEQscUJBQXFCLEdBQWdCLElBQUksV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3hELHFCQUFxQixHQUFHLENBQUMsQ0FBQztJQUMxQixnQkFBZ0IsR0FBRyxDQUFDLENBQUM7SUFDckIsc0JBQXNCLEdBQUcsQ0FBQyxDQUFDO0lBRW5CLE9BQU8sR0FBRyxFQUFjLENBQUM7SUFD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