libtess-ts 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"libtess.min.js","sources":["../src/Geom.ts","../src/Dict.ts","../src/Sweep.ts","../src/Normal.ts","../src/Render.ts","../src/types.ts","../src/Mesh.ts","../src/PriorityQHeap.ts","../src/PriorityQ.ts","../src/TessMono.ts","../src/GluTesselator.ts","../src/Assert.ts"],"sourcesContent":["import { Vertex, HalfEdge } from './Mesh';\n\nexport function vertEq(u: Vertex, v: Vertex) {\n return u.s === v.s && u.t === v.t;\n}\n\n// TRUE if u is lexicographically <= v\nexport function vertLeq(u: Vertex, v: Vertex) {\n return u.s < v.s || (u.s === v.s && u.t <= v.t);\n}\n\n// Transposed version of vertLeq\nexport function transLeq(u: Vertex, v: Vertex) {\n return u.t < v.t || (u.t === v.t && u.s <= v.s);\n}\n\nexport function edgeGoesLeft(e: HalfEdge) {\n return vertLeq(e.Sym.Org, e.Org);\n}\n\nexport function edgeGoesRight(e: HalfEdge) {\n return vertLeq(e.Org, e.Sym.Org);\n}\n\nexport function vertL1dist(u: Vertex, v: Vertex) {\n return Math.abs(u.s - v.s) + Math.abs(u.t - v.t);\n}\n\n// Given three vertices u,v,w such that VertLeq(u,v) && VertLeq(v,w),\n// evaluates the t-coord of the edge uw at the s-coord of vertex v.\n// Returns v.t - (uw)(v.s), ie. the signed distance from uw to v.\n// If uw is vertical (and thus passes thru v), the result is zero.\n//\n// The calculation is extremely accurate and stable, even when v\n// is very close to u or w. In particular if we set v.t = 0 and\n// let r be the negated result (this evaluates (uw)(v.s)), then\n// r is guaranteed to satisfy MIN(u.t,w.t) <= r <= MAX(u.t,w.t)\nexport function edgeEval(u: Vertex, v: Vertex, w: Vertex) {\n let gapL = v.s - u.s;\n let gapR = w.s - v.s;\n\n if (gapL + gapR > 0.0) {\n if (gapL < gapR) {\n return v.t - u.t + (u.t - w.t) * (gapL / (gapL + gapR));\n } else {\n return v.t - w.t + (w.t - u.t) * (gapR / (gapL + gapR));\n }\n }\n return 0.0;\n}\n\n// Returns a number whose sign matches edgeEval(u,v,w) but which\n// is cheaper to evaluate. Returns > 0, == 0, or < 0\n// as v is above, on, or below the edge uw\nexport function edgeSign(u: Vertex, v: Vertex, w: Vertex) {\n let gapL = v.s - u.s;\n let gapR = w.s - v.s;\n\n if (gapL + gapR > 0.0) {\n return (v.t - w.t) * gapL + (v.t - u.t) * gapR;\n }\n return 0.0;\n}\n\n// Transposed versions of edgeEval and edgeSign\n\n// Given three vertices u,v,w such that TransLeq(u,v) && TransLeq(v,w),\n// evaluates the s-coord of the edge uw at the t-coord of vertex v.\n// Returns v.s - (uw)(v.t), ie. the signed distance from uw to v.\n// If uw is vertical (and thus passes thru v), the result is zero.\n//\n// The calculation is extremely accurate and stable, even when v\n// is very close to u or w. In particular if we set v.s = 0 and\n// let r be the negated result (this evaluates (uw)(v.t)), then\n// r is guaranteed to satisfy MIN(u.s,w.s) <= r <= MAX(u.s,w.s)\nexport function transEval(u: Vertex, v: Vertex, w: Vertex) {\n let gapL = v.t - u.t;\n let gapR = w.t - v.t;\n\n if (gapL + gapR > 0.0) {\n if (gapL < gapR) {\n return v.s - u.s + (u.s - w.s) * (gapL / (gapL + gapR));\n } else {\n return v.s - w.s + (w.s - u.s) * (gapR / (gapL + gapR));\n }\n }\n return 0.0;\n}\n\n// Returns a number whose sign matches transEval(u,v,w) but cheaper\n// to evaluate. Returns > 0, == 0, or < 0\n// as v is above, on, or below the edge uw\nexport function transSign(u: Vertex, v: Vertex, w: Vertex) {\n let gapL = v.t - u.t;\n let gapR = w.t - v.t;\n\n if (gapL + gapR > 0.0) {\n return (v.s - w.s) * gapL + (v.s - u.s) * gapR;\n }\n return 0.0;\n}\n\n// Given parameters a,x,b,y returns the value (b*x+a*y)/(a+b),\n// or (x+y)/2 if a==b==0. It requires that a,b >= 0, and enforces\n// this in the rare case that one argument is slightly negative.\n// The implementation is extremely stable numerically.\n// In particular it guarantees that the result r satisfies\n// MIN(x,y) <= r <= MAX(x,y), and the results are very accurate\n// even when a and b differ greatly in magnitude\nexport function interpolate(a: number, x: number, b: number, y: number) {\n return (\n (a = a < 0 ? 0 : a),\n (b = b < 0 ? 0 : b),\n a <= b ? (b === 0 ? (x + y) / 2 : x + (y - x) * (a / (a + b))) : y + (x - y) * (b / (a + b))\n );\n}\n\n// Given edges (o1,d1) and (o2,d2), compute their point of intersection.\n// The computed point is guaranteed to lie in the intersection of the\n// bounding rectangles defined by each edge.\n//\n// Strategy: find the two middle vertices in the VertLeq ordering,\n// and interpolate the intersection s-value from these. Then repeat\n// using the TransLeq ordering to find the intersection t-value.\n// This is not the most efficient approach but it is very numerically stable\nexport function intersect(o1: Vertex, d1: Vertex, o2: Vertex, d2: Vertex, v: Vertex) {\n let z1, z2;\n let t;\n\n if (!vertLeq(o1, d1)) {\n t = o1;\n o1 = d1;\n d1 = t;\n }\n if (!vertLeq(o2, d2)) {\n t = o2;\n o2 = d2;\n d2 = t;\n }\n if (!vertLeq(o1, o2)) {\n t = o1;\n o1 = o2;\n o2 = t;\n t = d1;\n d1 = d2;\n d2 = t;\n }\n\n if (!vertLeq(o2, d1)) {\n // No intersection -- do our best\n v.s = (o2.s + d1.s) * 0.5;\n } else if (vertLeq(d1, d2)) {\n z1 = edgeEval(o1, o2, d1);\n z2 = edgeEval(o2, d1, d2);\n if (z1 + z2 < 0) {\n z1 = -z1;\n z2 = -z2;\n }\n v.s = interpolate(z1, o2.s, z2, d1.s);\n } else {\n z1 = edgeSign(o1, o2, d1);\n z2 = -edgeSign(o1, d2, d1);\n if (z1 + z2 < 0) {\n z1 = -z1;\n z2 = -z2;\n }\n v.s = interpolate(z1, o2.s, z2, d2.s);\n }\n\n // Now repeat the process for t\n\n if (!transLeq(o1, d1)) {\n t = o1;\n o1 = d1;\n d1 = t;\n }\n if (!transLeq(o2, d2)) {\n t = o2;\n o2 = d2;\n d2 = t;\n }\n if (!transLeq(o1, o2)) {\n t = o1;\n o1 = o2;\n o2 = t;\n t = d1;\n d1 = d2;\n d2 = t;\n }\n\n if (!transLeq(o2, d1)) {\n v.t = (o2.t + d1.t) * 0.5;\n } else if (transLeq(d1, d2)) {\n z1 = transEval(o1, o2, d1);\n z2 = transEval(o2, d1, d2);\n if (z1 + z2 < 0) {\n z1 = -z1;\n z2 = -z2;\n }\n v.t = interpolate(z1, o2.t, z2, d1.t);\n } else {\n z1 = transSign(o1, o2, d1);\n z2 = -transSign(o1, d2, d1);\n if (z1 + z2 < 0) {\n z1 = -z1;\n z2 = -z2;\n }\n v.t = interpolate(z1, o2.t, z2, d2.t);\n }\n}\n","import { DictNode } from './Mesh';\nimport { GluTesselator } from './GluTesselator';\nimport { vertLeq, edgeSign, edgeEval } from './Geom';\n\n// edgeLeq lives here (rather than in Sweep) so Dict's search/insertBefore\n// hot loops call it directly without indirection\nfunction edgeLeq(tess: GluTesselator, reg1: DictNode, reg2: DictNode): boolean {\n const ev = tess.event;\n const e1 = reg1.eUp;\n const e2 = reg2.eUp;\n\n if (e1.Sym.Org === ev) {\n if (e2.Sym.Org === ev) {\n if (vertLeq(e1.Org, e2.Org)) {\n return edgeSign(e2.Sym.Org, e1.Org, e2.Org) <= 0;\n }\n return edgeSign(e1.Sym.Org, e2.Org, e1.Org) >= 0;\n }\n return edgeSign(e2.Sym.Org, ev, e2.Org) <= 0;\n }\n if (e2.Sym.Org === ev) {\n return edgeSign(e1.Sym.Org, ev, e1.Org) >= 0;\n }\n\n const t1 = edgeEval(e1.Sym.Org, ev, e1.Org);\n const t2 = edgeEval(e2.Sym.Org, ev, e2.Org);\n return t1 >= t2;\n}\n\nexport class Dict {\n head: DictNode = new DictNode();\n frame: GluTesselator;\n\n constructor(frame: GluTesselator) {\n this.frame = frame;\n this.head.next = this.head;\n this.head.prev = this.head;\n }\n\n min() {\n return this.head.next;\n }\n\n max() {\n return this.head.prev;\n }\n\n insert(newNode: DictNode) {\n return this.insertBefore(this.head, newNode);\n }\n\n // Returns the node with the smallest key >= the given key,\n // or the head node (whose eUp is null) if no such key exists\n search(key: DictNode) {\n let node = this.head;\n do {\n node = node.next;\n } while (node.eUp !== null && !edgeLeq(this.frame, key, node));\n\n return node;\n }\n\n insertBefore(node: DictNode, newNode: DictNode) {\n do {\n node = node.prev;\n } while (node.eUp !== null && !edgeLeq(this.frame, node, newNode));\n\n newNode.next = node.next;\n node.next.prev = newNode;\n newNode.prev = node;\n node.next = newNode;\n\n return newNode;\n }\n\n delete(node: DictNode) {\n node.next.prev = node.prev;\n node.prev.next = node.next;\n }\n}\n","import { vertLeq, edgeSign, vertEq, intersect, vertL1dist, edgeGoesLeft, edgeGoesRight } from './Geom';\nimport { assert } from './Assert';\nimport { PriorityQ } from './PriorityQ';\nimport { WINDING } from './types';\nimport { DictNode, Vertex, Mesh, HalfEdge } from './Mesh';\nimport { Dict } from './Dict';\nimport { GluTesselator } from './GluTesselator';\n\n// Scratch objects reused across sweep events to reduce allocations\nlet isectScratch: Vertex | null = null;\nlet tmpRegionScratch: DictNode | null = null;\nconst scratchWeights: [number, number, number, number] = [0, 0, 0, 0];\nconst scratchData: [unknown, unknown, unknown, unknown] = [null, null, null, null];\nconst scratchCoords: [number, number, number] = [0, 0, 0];\n\nfunction regionBelow(r: DictNode) {\n return r.prev;\n}\n\nfunction regionAbove(r: DictNode) {\n return r.next;\n}\n\n// Invariants for the Edge Dictionary:\n// - each pair of adjacent edges e2=Succ(e1) satisfies EdgeLeq(e1,e2)\n// at any valid location of the sweep event\n// - if EdgeLeq(e2,e1) as well (at any valid sweep event), then e1 and e2\n// share a common endpoint\n// - for each e, e->Dst has been processed, but not e->Org\n// - each edge e satisfies VertLeq(e->Dst,event) && VertLeq(event,e->Org)\n// where \"event\" is the current sweep line event\n// - no edge e has zero length\n//\n// Invariants for the Mesh (the processed portion):\n// - the portion of the mesh left of the sweep line is a planar graph,\n// ie. there is *some* way to embed it in the plane\n// - no processed edge has zero length\n// - no two processed vertices have identical coordinates\n// - each \"inside\" region is monotone, ie. can be broken into two chains\n// of monotonically increasing vertices according to VertLeq(v1,v2)\n// - a non-invariant: these chains may intersect (very slightly)\n//\n// Invariants for the event vertex:\n// - if none of the edges incident to the event vertex have an activeRegion\n// (ie. none of these edges are in the edge dictionary), then the vertex\n// has only right-going edges\n// - if an edge is marked \"fixUpperEdge\" (a temporary edge introduced\n// by connectRightVertex), then it is the only right-going edge from\n// its associated vertex (these edges exist only when necessary)\nfunction addWinding(eDst: HalfEdge, eSrc: HalfEdge) {\n eDst.winding += eSrc.winding;\n eDst.Sym.winding += eSrc.Sym.winding;\n}\n\nfunction deleteRegion(tess: GluTesselator, reg: DictNode) {\n // It was created with zero winding number, so it better be\n // deleted with zero winding number (ie. it better not get merged\n // with a real edge)\n assert(!reg.fixUpperEdge || reg.eUp.winding === 0);\n reg.eUp.activeRegion = null;\n tess.dict.delete(reg);\n}\n\nfunction fixUpperEdge(tess: GluTesselator, reg: DictNode, newEdge: HalfEdge) {\n // Replace an upper edge which needs fixing (see connectRightVertex)\n\n tess.mesh.delete(reg.eUp);\n reg.fixUpperEdge = false;\n reg.eUp = newEdge;\n newEdge.activeRegion = reg;\n}\n\nfunction topLeftRegion(tess: GluTesselator, reg: DictNode) {\n let org = reg.eUp.Org;\n let e;\n\n // Find the region above the uppermost edge with the same origin\n do {\n reg = regionAbove(reg);\n } while (reg.eUp.Org === org);\n\n // If the edge above was a temporary edge introduced by ConnectRightVertex,\n // now is the time to fix it\n if (reg.fixUpperEdge) {\n e = tess.mesh.connect(regionBelow(reg).eUp.Sym, reg.eUp.Lnext);\n fixUpperEdge(tess, reg, e);\n reg = regionAbove(reg);\n }\n return reg;\n}\n\nfunction topRightRegion(reg: DictNode) {\n let dst = reg.eUp.Sym.Org;\n // Find the region above the uppermost edge with the same destination\n do {\n reg = regionAbove(reg);\n } while (reg.eUp.Sym.Org === dst);\n return reg;\n}\n\nfunction addRegionBelow(tess: GluTesselator, regAbove: DictNode, eNewUp: HalfEdge) {\n // Add a new active region to the sweep line, *somewhere* below \"regAbove\"\n // (according to where the new edge belongs in the sweep-line dictionary).\n // The upper edge of the new region will be \"eNewUp\".\n // Winding number and \"inside\" flag are not updated.\n //\n const regNew = new DictNode();\n regNew.eUp = eNewUp;\n tess.dict.insertBefore(regAbove, regNew);\n regNew.fixUpperEdge = false;\n regNew.sentinel = false;\n regNew.dirty = false;\n\n eNewUp.activeRegion = regNew;\n return regNew;\n}\n\nfunction isWindingInside(tess: GluTesselator, n: number) {\n switch (tess.windingRule_) {\n case WINDING.ODD:\n return (n & 1) !== 0;\n case WINDING.NONZERO:\n return n !== 0;\n case WINDING.POSITIVE:\n return n > 0;\n case WINDING.NEGATIVE:\n return n < 0;\n case WINDING.ABS_GEQ_TWO:\n return n >= 2 || n <= -2;\n }\n\n throw new Error('Invalid winding rule');\n}\n\nfunction computeWinding(tess: GluTesselator, reg: DictNode) {\n reg.windingNumber = regionAbove(reg).windingNumber + reg.eUp.winding;\n reg.inside = isWindingInside(tess, reg.windingNumber);\n}\n\nfunction finishRegion(tess: GluTesselator, reg: DictNode) {\n // Delete a region from the sweep line. This happens when the upper\n // and lower chains of a region meet (at a vertex on the sweep line).\n // The \"inside\" flag is copied to the appropriate mesh face (we could\n // not do this before -- since the structure of the mesh is always\n // changing, this face may not have even existed until now).\n //\n const e = reg.eUp;\n const f = e.Lface;\n\n f.inside = reg.inside;\n f.anEdge = e; // optimization for tessMeshTessellateMonoRegion()\n deleteRegion(tess, reg);\n}\n\nfunction finishLeftRegions(tess: GluTesselator, regFirst: DictNode, regLast: DictNode | null) {\n // We are given a vertex with one or more left-going edges. All affected\n // edges should be in the edge dictionary. Starting at regFirst->eUp,\n // we walk down deleting all regions where both edges have the same\n // origin vOrg. At the same time we copy the \"inside\" flag from the\n // active region to the face, since at this point each face will belong\n // to at most one region (this was not necessarily true until this point\n // in the sweep). The walk stops at the region above regLast; if regLast\n // is NULL we walk as far as possible. At the same time we relink the\n // mesh if necessary, so that the ordering of edges around vOrg is the\n // same as in the dictionary.\n //\n let e;\n let reg = null;\n let regPrev = regFirst;\n let ePrev = regFirst.eUp;\n\n while (regPrev !== regLast) {\n regPrev.fixUpperEdge = false; // placement was OK\n reg = regionBelow(regPrev);\n e = reg.eUp;\n if (e.Org != ePrev.Org) {\n if (!reg.fixUpperEdge) {\n // Remove the last left-going edge. Even though there are no further\n // edges in the dictionary with this origin, there may be further\n // such edges in the mesh (if we are adding left edges to a vertex\n // that has already been processed). Thus it is important to call\n // FinishRegion rather than just DeleteRegion\n finishRegion(tess, regPrev);\n break;\n }\n // If the edge below was a temporary edge introduced by\n // ConnectRightVertex, now is the time to fix it\n e = tess.mesh.connect(ePrev.Onext.Sym, e.Sym);\n fixUpperEdge(tess, reg, e);\n }\n\n // Relink edges so that ePrev->Onext == e\n if (ePrev.Onext !== e) {\n tess.mesh.splice(e.Sym.Lnext, e);\n tess.mesh.splice(ePrev, e);\n }\n finishRegion(tess, regPrev); // may change reg->eUp\n ePrev = reg.eUp;\n regPrev = reg;\n }\n return ePrev;\n}\n\nfunction addRightEdges(\n tess: GluTesselator,\n regUp: DictNode,\n eFirst: HalfEdge,\n eLast: HalfEdge,\n eTopLeft: HalfEdge | null,\n cleanUp: boolean\n) {\n // Purpose: insert right-going edges into the edge dictionary, and update\n // winding numbers and mesh connectivity appropriately. All right-going\n // edges share a common origin vOrg. Edges are inserted CCW starting at\n // eFirst; the last edge inserted is eLast->Oprev. If vOrg has any\n // left-going edges already processed, then eTopLeft must be the edge\n // such that an imaginary upward vertical segment from vOrg would be\n // contained between eTopLeft->Oprev and eTopLeft; otherwise eTopLeft\n // should be NULL.\n //\n let reg, regPrev;\n let e, ePrev;\n let firstTime = true;\n\n // Insert the new right-going edges in the dictionary\n e = eFirst;\n do {\n addRegionBelow(tess, regUp, e.Sym);\n e = e.Onext;\n } while (e !== eLast);\n\n // Walk *all* right-going edges from e->Org, in the dictionary order,\n // updating the winding numbers of each region, and re-linking the mesh\n // edges to match the dictionary ordering (if necessary).\n if (eTopLeft === null) {\n eTopLeft = regionBelow(regUp).eUp.Sym.Onext;\n }\n regPrev = regUp;\n ePrev = eTopLeft;\n while (true) {\n reg = regionBelow(regPrev);\n e = reg.eUp.Sym;\n if (e.Org !== ePrev.Org) break;\n\n if (e.Onext !== ePrev) {\n // Unlink e from its current position, and relink below ePrev\n tess.mesh.splice(e.Sym.Lnext, e);\n tess.mesh.splice(ePrev.Sym.Lnext, e);\n }\n // Compute the winding number and \"inside\" flag for the new regions\n reg.windingNumber = regPrev.windingNumber - e.winding;\n reg.inside = isWindingInside(tess, reg.windingNumber);\n\n // Check for two outgoing edges with same slope -- process these\n // before any intersection tests (see example in tessComputeInterior)\n regPrev.dirty = true;\n if (!firstTime && checkForRightSplice(tess, regPrev)) {\n addWinding(e, ePrev);\n deleteRegion(tess, regPrev);\n tess.mesh.delete(ePrev);\n }\n firstTime = false;\n regPrev = reg;\n ePrev = e;\n }\n regPrev.dirty = true;\n\n if (cleanUp) {\n // Check for intersections between newly adjacent edges\n walkDirtyRegions(tess, regPrev);\n }\n}\n\nfunction callCombine(\n tess: GluTesselator,\n isect: Vertex,\n data: [unknown, unknown, unknown, unknown],\n weights: [number, number, number, number],\n needed: boolean\n) {\n isect.data = null;\n\n if (tess.onCombine_) {\n scratchCoords[0] = isect.coords[0];\n scratchCoords[1] = isect.coords[1];\n scratchCoords[2] = isect.coords[2];\n isect.data = tess.onCombine_(scratchCoords, data, weights, tess.polygonData);\n }\n\n if (isect.data === null) {\n if (!needed) {\n isect.data = data[0];\n } else {\n tess.callErrorOrErrorData(100156); // GLU_TESS_NEED_COMBINE_CALLBACK\n tess.noEmit_ = true;\n }\n }\n}\n\nfunction spliceMergeVertices(tess: GluTesselator, e1: HalfEdge, e2: HalfEdge) {\n // Two vertices with identical coordinates are combined into one.\n // e1->Org is kept, while e2->Org is discarded.\n //\n if (tess.onCombine_) {\n scratchData[0] = e1.Org.data;\n scratchData[1] = e2.Org.data;\n scratchData[2] = null;\n scratchData[3] = null;\n scratchWeights[0] = 0.5;\n scratchWeights[1] = 0.5;\n scratchWeights[2] = 0.0;\n scratchWeights[3] = 0.0;\n callCombine(tess, e1.Org, scratchData, scratchWeights, false);\n }\n tess.mesh.splice(e1, e2);\n}\n\nfunction vertexWeights(\n isect: Vertex,\n org: Vertex,\n dst: Vertex,\n weights?: [number, number, number, number],\n weightIndex?: number\n) {\n // Find some weights which describe how the intersection vertex is\n // a linear combination of \"org\" and \"dest\". Each of the two edges\n // which generated \"isect\" is allocated 50% of the weight; each edge\n // splits the weight between its org and dst according to the\n // relative distance to \"isect\".\n //\n let t1 = vertL1dist(org, isect);\n let t2 = vertL1dist(dst, isect);\n let w0 = (0.5 * t2) / (t1 + t2);\n let w1 = (0.5 * t1) / (t1 + t2);\n\n if (weights !== undefined && weightIndex !== undefined) {\n weights[weightIndex] = w0;\n weights[weightIndex + 1] = w1;\n }\n\n isect.coords[0] += w0 * org.coords[0] + w1 * dst.coords[0];\n isect.coords[1] += w0 * org.coords[1] + w1 * dst.coords[1];\n isect.coords[2] += w0 * org.coords[2] + w1 * dst.coords[2];\n}\n\nfunction getIntersectData(\n tess: GluTesselator,\n isect: Vertex,\n orgUp: Vertex,\n dstUp: Vertex,\n orgLo: Vertex,\n dstLo: Vertex\n) {\n // We've computed a new intersection point, now we need a \"data\" pointer\n // from the user so that we can refer to this new vertex in the\n // rendering callbacks.\n //\n scratchWeights[0] = 0;\n scratchWeights[1] = 0;\n scratchWeights[2] = 0;\n scratchWeights[3] = 0;\n scratchData[0] = orgUp.data;\n scratchData[1] = dstUp.data;\n scratchData[2] = orgLo.data;\n scratchData[3] = dstLo.data;\n\n isect.coords[0] = isect.coords[1] = isect.coords[2] = 0;\n\n vertexWeights(isect, orgUp, dstUp, scratchWeights, 0);\n vertexWeights(isect, orgLo, dstLo, scratchWeights, 2);\n\n callCombine(tess, isect, scratchData, scratchWeights, true);\n}\n\nfunction checkForRightSplice(tess: GluTesselator, regUp: DictNode) {\n // Check the upper and lower edge of \"regUp\", to make sure that the\n // eUp->Org is above eLo, or eLo->Org is below eUp (depending on which\n // origin is leftmost).\n //\n // The main purpose is to splice right-going edges with the same\n // dest vertex and nearly identical slopes (ie. we can't distinguish\n // the slopes numerically). However the splicing can also help us\n // to recover from numerical errors. For example, suppose at one\n // point we checked eUp and eLo, and decided that eUp->Org is barely\n // above eLo. Then later, we split eLo into two edges (eg. from\n // a splice operation like this one). This can change the result of\n // our test so that now eUp->Org is incident to eLo, or barely below it.\n // We must correct this condition to maintain the dictionary invariants.\n //\n // One possibility is to check these edges for intersection again\n // (ie. CheckForIntersect). This is what we do if possible. However\n // CheckForIntersect requires that tess->event lies between eUp and eLo,\n // so that it has something to fall back on when the intersection\n // calculation gives us an unusable answer. So, for those cases where\n // we can't check for intersection, this routine fixes the problem\n // by just splicing the offending vertex into the other edge.\n // This is a guaranteed solution, no matter how degenerate things get.\n // Basically this is a combinatorial solution to a numerical problem.\n //\n let regLo = regionBelow(regUp);\n const eUp = regUp.eUp;\n const eLo = regLo.eUp;\n\n if (eUp.Org.s < eLo.Org.s || (eUp.Org.s === eLo.Org.s && eUp.Org.t <= eLo.Org.t)) {\n if (edgeSign(eLo.Sym.Org, eUp.Org, eLo.Org) > 0) return false;\n\n // eUp->Org appears to be below eLo\n if (!vertEq(eUp.Org, eLo.Org)) {\n // Splice eUp->Org into eLo\n tess.mesh.splitEdge(eLo.Sym);\n tess.mesh.splice(eUp, eLo.Sym.Lnext);\n regUp.dirty = regLo.dirty = true;\n } else if (eUp.Org !== eLo.Org) {\n // merge the two vertices, discarding eUp->Org\n tess.pq.delete(eUp.Org.pqHandle);\n spliceMergeVertices(tess, eLo.Sym.Lnext, eUp);\n }\n } else {\n if (edgeSign(eUp.Sym.Org, eLo.Org, eUp.Org) < 0) return false;\n\n // eLo->Org appears to be above eUp, so splice eLo->Org into eUp\n regionAbove(regUp).dirty = regUp.dirty = true;\n tess.mesh.splitEdge(eUp.Sym);\n tess.mesh.splice(eLo.Sym.Lnext, eUp);\n }\n return true;\n}\n\nfunction checkForLeftSplice(tess: GluTesselator, regUp: DictNode) {\n // Check the upper and lower edge of \"regUp\", to make sure that the\n // eUp->Dst is above eLo, or eLo->Dst is below eUp (depending on which\n // destination is rightmost).\n //\n // Theoretically, this should always be true. However, splitting an edge\n // into two pieces can change the results of previous tests. For example,\n // suppose at one point we checked eUp and eLo, and decided that eUp->Dst\n // is barely above eLo. Then later, we split eLo into two edges (eg. from\n // a splice operation like this one). This can change the result of\n // the test so that now eUp->Dst is incident to eLo, or barely below it.\n // We must correct this condition to maintain the dictionary invariants\n // (otherwise new edges might get inserted in the wrong place in the\n // dictionary, and bad stuff will happen).\n //\n // We fix the problem by just splicing the offending vertex into the\n // other edge.\n //\n let regLo = regionBelow(regUp);\n const eUp = regUp.eUp;\n const eLo = regLo.eUp;\n let e;\n\n if (eUp.Sym.Org.s < eLo.Sym.Org.s || (eUp.Sym.Org.s === eLo.Sym.Org.s && eUp.Sym.Org.t <= eLo.Sym.Org.t)) {\n if (edgeSign(eUp.Sym.Org, eLo.Sym.Org, eUp.Org) < 0) return false;\n\n // eLo->Dst is above eUp, so splice eLo->Dst into eUp\n regionAbove(regUp).dirty = regUp.dirty = true;\n e = tess.mesh.splitEdge(eUp);\n tess.mesh.splice(eLo.Sym, e);\n e.Lface.inside = regUp.inside;\n } else {\n if (edgeSign(eLo.Sym.Org, eUp.Sym.Org, eLo.Org) > 0) return false;\n\n // eUp->Dst is below eLo, so splice eUp->Dst into eLo\n regUp.dirty = regLo.dirty = true;\n e = tess.mesh.splitEdge(eLo);\n tess.mesh.splice(eUp.Lnext, eLo.Sym);\n e.Sym.Lface.inside = regUp.inside;\n }\n return true;\n}\n\nfunction checkForIntersect(tess: GluTesselator, regUp: DictNode) {\n // Check the upper and lower edges of the given region to see if\n // they intersect. If so, create the intersection and add it\n // to the data structures.\n //\n // Returns TRUE if adding the new intersection resulted in a recursive\n // call to AddRightEdges(); in this case all \"dirty\" regions have been\n // checked for intersections, and possibly regUp has been deleted.\n //\n let regLo = regionBelow(regUp);\n let eUp = regUp.eUp;\n let eLo = regLo.eUp;\n const orgUp = eUp.Org;\n const orgLo = eLo.Org;\n let dstUp = eUp.Sym.Org;\n let dstLo = eLo.Sym.Org;\n let tMinUp, tMaxLo;\n const isect = isectScratch || (isectScratch = new Vertex());\n let orgMin;\n let e;\n\n if (orgUp === orgLo) return false; // right endpoints are the same\n\n tMinUp = Math.min(orgUp.t, dstUp.t);\n tMaxLo = Math.max(orgLo.t, dstLo.t);\n if (tMinUp > tMaxLo) return false; // t ranges do not overlap\n\n if (vertLeq(orgUp, orgLo)) {\n if (edgeSign(dstLo, orgUp, orgLo) > 0) return false;\n } else {\n if (edgeSign(dstUp, orgLo, orgUp) < 0) return false;\n }\n\n // At this point the edges intersect, at least marginally\n intersect(dstUp, orgUp, dstLo, orgLo, isect);\n if (isect.s < tess.event.s || (isect.s === tess.event.s && isect.t <= tess.event.t)) {\n // The intersection point lies slightly to the left of the sweep line,\n // so move it until it''s slightly to the right of the sweep line.\n // (If we had perfect numerical precision, this would never happen\n // in the first place). The easiest and safest thing to do is\n // replace the intersection by tess->event.\n isect.s = tess.event.s;\n isect.t = tess.event.t;\n }\n // Similarly, if the computed intersection lies to the right of the\n // rightmost origin (which should rarely happen), it can cause\n // unbelievable inefficiency on sufficiently degenerate inputs.\n // (If you have the test program, try running test54.d with the\n // \"X zoom\" option turned on).\n orgMin = orgUp.s < orgLo.s || (orgUp.s === orgLo.s && orgUp.t <= orgLo.t) ? orgUp : orgLo;\n if (orgMin.s < isect.s || (orgMin.s === isect.s && orgMin.t <= isect.t)) {\n isect.s = orgMin.s;\n isect.t = orgMin.t;\n }\n\n if (vertEq(isect, orgUp) || vertEq(isect, orgLo)) {\n // Easy case -- intersection at one of the right endpoints\n checkForRightSplice(tess, regUp);\n return false;\n }\n\n if (\n (!vertEq(dstUp, tess.event) && edgeSign(dstUp, tess.event, isect) >= 0) ||\n (!vertEq(dstLo, tess.event) && edgeSign(dstLo, tess.event, isect) <= 0)\n ) {\n // Very unusual -- the new upper or lower edge would pass on the\n // wrong side of the sweep event, or through it. This can happen\n // due to very small numerical errors in the intersection calculation\n if (dstLo === tess.event) {\n // Splice dstLo into eUp, and process the new region(s)\n tess.mesh.splitEdge(eUp.Sym);\n tess.mesh.splice(eLo.Sym, eUp);\n regUp = topLeftRegion(tess, regUp);\n eUp = regionBelow(regUp).eUp;\n finishLeftRegions(tess, regionBelow(regUp), regLo);\n addRightEdges(tess, regUp, eUp.Sym.Lnext, eUp, eUp, true);\n return true;\n }\n if (dstUp === tess.event) {\n // Splice dstUp into eLo, and process the new region(s)\n tess.mesh.splitEdge(eLo.Sym);\n tess.mesh.splice(eUp.Lnext, eLo.Sym.Lnext);\n regLo = regUp;\n regUp = topRightRegion(regUp);\n e = regionBelow(regUp).eUp.Sym.Onext;\n regLo.eUp = eLo.Sym.Lnext;\n eLo = finishLeftRegions(tess, regLo, null);\n addRightEdges(tess, regUp, eLo.Onext, eUp.Sym.Onext, e, true);\n return true;\n }\n // Special case: called from ConnectRightVertex. If either\n // edge passes on the wrong side of tess->event, split it\n // (and wait for ConnectRightVertex to splice it appropriately)\n if (edgeSign(dstUp, tess.event, isect) >= 0) {\n regionAbove(regUp).dirty = regUp.dirty = true;\n tess.mesh.splitEdge(eUp.Sym);\n eUp.Org.s = tess.event.s;\n eUp.Org.t = tess.event.t;\n }\n if (edgeSign(dstLo, tess.event, isect) <= 0) {\n regUp.dirty = regLo.dirty = true;\n tess.mesh.splitEdge(eLo.Sym);\n eLo.Org.s = tess.event.s;\n eLo.Org.t = tess.event.t;\n }\n // leave the rest for ConnectRightVertex\n return false;\n }\n\n // General case -- split both edges, splice into new vertex.\n // When we do the splice operation, the order of the arguments is\n // arbitrary as far as correctness goes. However, when the operation\n // creates a new face, the work done is proportional to the size of\n // the new face. We expect the faces in the processed part of\n // the mesh (ie. eUp->Lface) to be smaller than the faces in the\n // unprocessed original contours (which will be eLo->Oprev->Lface).\n tess.mesh.splitEdge(eUp.Sym);\n tess.mesh.splitEdge(eLo.Sym);\n tess.mesh.splice(eLo.Sym.Lnext, eUp);\n eUp.Org.s = isect.s;\n eUp.Org.t = isect.t;\n eUp.Org.pqHandle = tess.pq.insert(eUp.Org);\n getIntersectData(tess, eUp.Org, orgUp, dstUp, orgLo, dstLo);\n regionAbove(regUp).dirty = regUp.dirty = regLo.dirty = true;\n return false;\n}\n\nfunction walkDirtyRegions(tess: GluTesselator, regUp: DictNode) {\n // When the upper or lower edge of any region changes, the region is\n // marked \"dirty\". This routine walks through all the dirty regions\n // and makes sure that the dictionary invariants are satisfied\n // (see the comments at the beginning of this file). Of course\n // new dirty regions can be created as we make changes to restore\n // the invariants.\n //\n let regLo = regionBelow(regUp);\n let eUp, eLo;\n\n while (true) {\n // Find the lowest dirty region (we walk from the bottom up)\n while (regLo.dirty) {\n regUp = regLo;\n regLo = regionBelow(regLo);\n }\n if (!regUp.dirty) {\n regLo = regUp;\n regUp = regionAbove(regUp);\n if (regUp === null || !regUp.dirty) {\n // We've walked all the dirty regions\n return;\n }\n }\n regUp.dirty = false;\n eUp = regUp.eUp;\n eLo = regLo.eUp;\n\n if (eUp.Sym.Org !== eLo.Sym.Org) {\n // Check that the edge ordering is obeyed at the Dst vertices\n if (checkForLeftSplice(tess, regUp)) {\n // If the upper or lower edge was marked fixUpperEdge, then\n // we no longer need it (since these edges are needed only for\n // vertices which otherwise have no right-going edges).\n if (regLo.fixUpperEdge) {\n deleteRegion(tess, regLo);\n tess.mesh.delete(eLo);\n regLo = regionBelow(regUp);\n eLo = regLo.eUp;\n } else if (regUp.fixUpperEdge) {\n deleteRegion(tess, regUp);\n tess.mesh.delete(eUp);\n regUp = regionAbove(regLo);\n eUp = regUp.eUp;\n }\n }\n }\n if (eUp.Org !== eLo.Org) {\n if (\n eUp.Sym.Org !== eLo.Sym.Org &&\n !regUp.fixUpperEdge &&\n !regLo.fixUpperEdge &&\n (eUp.Sym.Org === tess.event || eLo.Sym.Org === tess.event)\n ) {\n // When all else fails in CheckForIntersect(), it uses tess->event\n // as the intersection location. To make this possible, it requires\n // that tess->event lie between the upper and lower edges, and also\n // that neither of these is marked fixUpperEdge (since in the worst\n // case it might splice one of these edges into tess->event, and\n // violate the invariant that fixable edges are the only right-going\n // edge from their associated vertex).\n if (checkForIntersect(tess, regUp)) {\n // WalkDirtyRegions() was called recursively; we're done\n return;\n }\n } else {\n // Even though we can't use CheckForIntersect(), the Org vertices\n // may violate the dictionary edge ordering -- check and correct this\n checkForRightSplice(tess, regUp);\n }\n }\n if (eUp.Org === eLo.Org && eUp.Sym.Org === eLo.Sym.Org) {\n // A degenerate loop consisting of only two edges -- delete it\n addWinding(eLo, eUp);\n deleteRegion(tess, regUp);\n tess.mesh.delete(eUp);\n regUp = regionAbove(regLo);\n }\n }\n}\n\nfunction connectRightVertex(tess: GluTesselator, regUp: DictNode, eBottomLeft: HalfEdge) {\n // Purpose: connect a \"right\" vertex vEvent (one where all edges go left)\n // to the unprocessed portion of the mesh. Since there are no right-going\n // edges, two regions (one above vEvent and one below) are being merged\n // into one. \"regUp\" is the upper of these two regions.\n //\n // There are two reasons for doing this (adding a right-going edge):\n // - if the two regions being merged are \"inside\", we must add an edge\n // to keep them separated (the combined region would not be monotone).\n // - in any case, we must leave some record of vEvent in the dictionary,\n // so that we can merge vEvent with features that we have not seen yet.\n // For example, maybe there is a vertical edge which passes just to\n // the right of vEvent; we would like to splice vEvent into this edge.\n //\n // However, we don't want to connect vEvent to just any vertex. We don''t\n // want the new edge to cross any other edges; otherwise we will create\n // intersection vertices even when the input data had no self-intersections.\n // (This is a bad thing; if the user's input data has no intersections,\n // we don't want to generate any false intersections ourselves.)\n //\n // Our eventual goal is to connect vEvent to the leftmost unprocessed\n // vertex of the combined region (the union of regUp and regLo).\n // But because of unseen vertices with all right-going edges, and also\n // new vertices which may be created by edge intersections, we don''t\n // know where that leftmost unprocessed vertex is. In the meantime, we\n // connect vEvent to the closest vertex of either chain, and mark the region\n // as \"fixUpperEdge\". This flag says to delete and reconnect this edge\n // to the next processed vertex on the boundary of the combined region.\n // Quite possibly the vertex we connected to will turn out to be the\n // closest one, in which case we won''t need to make any changes.\n //\n let eNew;\n let eTopLeft = eBottomLeft.Onext;\n let regLo = regionBelow(regUp);\n let eUp = regUp.eUp;\n let eLo = regLo.eUp;\n let degenerate = false;\n\n if (eUp.Sym.Org !== eLo.Sym.Org) {\n checkForIntersect(tess, regUp);\n }\n\n // Possible new degeneracies: upper or lower edge of regUp may pass\n // through vEvent, or may coincide with new intersection vertex\n if (vertEq(eUp.Org, tess.event)) {\n tess.mesh.splice(eTopLeft.Sym.Lnext, eUp);\n regUp = topLeftRegion(tess, regUp);\n eTopLeft = regionBelow(regUp).eUp;\n finishLeftRegions(tess, regionBelow(regUp), regLo);\n degenerate = true;\n }\n if (vertEq(eLo.Org, tess.event)) {\n tess.mesh.splice(eBottomLeft, eLo.Sym.Lnext);\n eBottomLeft = finishLeftRegions(tess, regLo, null);\n degenerate = true;\n }\n if (degenerate) {\n addRightEdges(tess, regUp, eBottomLeft.Onext, eTopLeft, eTopLeft, true);\n return;\n }\n\n // Non-degenerate situation -- need to add a temporary, fixable edge\n // Connect to the closer of eLo->Org, eUp->Org\n if (vertLeq(eLo.Org, eUp.Org)) {\n eNew = eLo.Sym.Lnext;\n } else {\n eNew = eUp;\n }\n eNew = tess.mesh.connect(eBottomLeft.Onext.Sym, eNew);\n\n // Prevent cleanup, otherwise eNew might disappear before we've even\n // had a chance to mark it as a temporary edge\n addRightEdges(tess, regUp, eNew, eNew.Onext, eNew.Onext, false);\n eNew.Sym.activeRegion!.fixUpperEdge = true;\n walkDirtyRegions(tess, regUp);\n}\n\nfunction connectLeftDegenerate(tess: GluTesselator, regUp: DictNode, vEvent: Vertex) {\n // The event vertex lies exacty on an already-processed edge or vertex.\n // Adding the new vertex involves splicing it into the already-processed\n // part of the mesh.\n //\n let e, eTopLeft, eTopRight, eLast;\n let reg;\n\n e = regUp.eUp;\n if (vertEq(e.Org, vEvent)) {\n // e->Org is an unprocessed vertex - just combine them, and wait\n // for e->Org to be pulled from the queue\n\n spliceMergeVertices(tess, e, vEvent.anEdge);\n return;\n }\n\n if (!vertEq(e.Sym.Org, vEvent)) {\n // General case -- splice vEvent into edge e which passes through it\n tess.mesh.splitEdge(e.Sym);\n if (regUp.fixUpperEdge) {\n // This edge was fixable -- delete unused portion of original edge\n tess.mesh.delete(e.Onext);\n regUp.fixUpperEdge = false;\n }\n tess.mesh.splice(vEvent.anEdge, e);\n sweepEvent(tess, vEvent); // recurse\n return;\n }\n\n // vEvent coincides with e->Dst, which has already been processed.\n // Splice in the additional right-going edges.\n\n regUp = topRightRegion(regUp);\n reg = regionBelow(regUp);\n eTopRight = reg.eUp.Sym;\n eTopLeft = eLast = eTopRight.Onext;\n if (reg.fixUpperEdge) {\n // Here e->Dst has only a single fixable edge going right.\n // We can delete it since now we have some real right-going edges\n assert(eTopLeft !== eTopRight); // there are some left edges too\n deleteRegion(tess, reg);\n tess.mesh.delete(eTopRight);\n eTopRight = eTopLeft.Sym.Lnext;\n }\n tess.mesh.splice(vEvent.anEdge, eTopRight);\n if (!edgeGoesLeft(eTopLeft)) {\n // e->Dst had no left-going edges -- indicate this to AddRightEdges()\n eTopLeft = null;\n }\n addRightEdges(tess, regUp, eTopRight.Onext, eLast, eTopLeft, true);\n}\n\nfunction connectLeftVertex(tess: GluTesselator, vEvent: Vertex) {\n // Purpose: connect a \"left\" vertex (one where both edges go right)\n // to the processed portion of the mesh. Let R be the active region\n // containing vEvent, and let U and L be the upper and lower edge\n // chains of R. There are two possibilities:\n //\n // - the normal case: split R into two regions, by connecting vEvent to\n // the rightmost vertex of U or L lying to the left of the sweep line\n //\n // - the degenerate case: if vEvent is close enough to U or L, we\n // merge vEvent into that edge chain. The subcases are:\n // - merging with the rightmost vertex of U or L\n // - merging with the active edge of U or L\n // - merging with an already-processed portion of U or L\n //\n let regUp, regLo, reg;\n let eUp, eLo, eNew;\n const tmp = tmpRegionScratch || (tmpRegionScratch = new DictNode());\n\n // Get a pointer to the active region containing vEvent\n tmp.eUp = vEvent.anEdge.Sym; // tessDictListSearch\n regUp = tess.dict.search(tmp);\n regLo = regionBelow(regUp);\n if (!regLo) {\n // This may happen if the input polygon is coplanar\n return;\n }\n eUp = regUp.eUp;\n eLo = regLo.eUp;\n\n // Try merging with U or L first\n if (edgeSign(eUp.Sym.Org, vEvent, eUp.Org) === 0.0) {\n connectLeftDegenerate(tess, regUp, vEvent);\n return;\n }\n\n // Connect vEvent to rightmost processed vertex of either chain.\n // e->Dst is the vertex that we will connect to vEvent.\n reg = vertLeq(eLo.Sym.Org, eUp.Sym.Org) ? regUp : regLo;\n\n if (regUp.inside || reg.fixUpperEdge) {\n if (reg === regUp) {\n eNew = tess.mesh.connect(vEvent.anEdge.Sym, eUp.Lnext);\n } else {\n let tempHalfEdge = tess.mesh.connect(eLo.Sym.Onext.Sym, vEvent.anEdge);\n eNew = tempHalfEdge.Sym;\n }\n if (reg.fixUpperEdge) {\n fixUpperEdge(tess, reg, eNew);\n } else {\n computeWinding(tess, addRegionBelow(tess, regUp, eNew));\n }\n sweepEvent(tess, vEvent);\n } else {\n // The new vertex is in a region which does not belong to the polygon.\n // We don''t need to connect this vertex to the rest of the mesh.\n addRightEdges(tess, regUp, vEvent.anEdge, vEvent.anEdge, null, true);\n }\n}\n\nfunction sweepEvent(tess: GluTesselator, vEvent: Vertex) {\n // Does everything necessary when the sweep line crosses a vertex.\n // Updates the mesh and the edge dictionary.\n //\n\n tess.event = vEvent;\n\n // Check if this vertex is the right endpoint of an edge that is\n // already in the dictionary. In this case we don't need to waste\n // time searching for the location to insert new edges.\n let e = vEvent.anEdge;\n while (e.activeRegion === null) {\n e = e.Onext;\n if (e === vEvent.anEdge) {\n // All edges go right -- not incident to any processed edges\n connectLeftVertex(tess, vEvent);\n return;\n }\n }\n\n // Processing consists of two phases: first we \"finish\" all the\n // active regions where both the upper and lower edges terminate\n // at vEvent (ie. vEvent is closing off these regions).\n // We mark these faces \"inside\" or \"outside\" the polygon according\n // to their winding number, and delete the edges from the dictionary.\n // This takes care of all the left-going edges from vEvent.\n let regUp = topLeftRegion(tess, e.activeRegion!);\n\n let reg = regionBelow(regUp);\n const eTopLeft = reg.eUp;\n let eBottomLeft = finishLeftRegions(tess, reg, null);\n\n // Next we process all the right-going edges from vEvent. This\n // involves adding the edges to the dictionary, and creating the\n // associated \"active regions\" which record information about the\n // regions between adjacent dictionary edges.\n if (eBottomLeft.Onext === eTopLeft) {\n // No right-going edges -- add a temporary \"fixable\" edge\n connectRightVertex(tess, regUp, eBottomLeft);\n } else {\n addRightEdges(tess, regUp, eBottomLeft.Onext, eTopLeft, eTopLeft, true);\n }\n}\n\nfunction addSentinel(tess: GluTesselator, smin: number, smax: number, t: number) {\n // We add two sentinel edges above and below all other edges,\n // to avoid special cases at the top and bottom.\n //\n const reg = new DictNode();\n let e = tess.mesh.makeEdge();\n\n e.Org.s = smax;\n e.Org.t = t;\n e.Sym.Org.s = smin;\n e.Sym.Org.t = t;\n tess.event = e.Sym.Org; // initialize it\n\n reg.eUp = e;\n reg.windingNumber = 0;\n reg.inside = false;\n reg.fixUpperEdge = false;\n reg.sentinel = true;\n reg.dirty = false;\n tess.dict.insert(reg);\n}\n\nfunction initEdgeDict(tess: GluTesselator) {\n // We maintain an ordering of edge intersections with the sweep line.\n // This order is maintained in a dynamic dictionary.\n //\n tess.dict = new Dict(tess);\n\n let w = tess.bmax[0] - tess.bmin[0];\n let h = tess.bmax[1] - tess.bmin[1];\n\n let smin = tess.bmin[0] - w;\n let smax = tess.bmax[0] + w;\n let tmin = tess.bmin[1] - h;\n let tmax = tess.bmax[1] + h;\n\n addSentinel(tess, smin, smax, tmin);\n addSentinel(tess, smin, smax, tmax);\n}\n\nfunction doneEdgeDict(tess: GluTesselator) {\n let reg;\n while ((reg = tess.dict.min()).eUp !== null) {\n deleteRegion(tess, reg);\n }\n}\n\nfunction removeDegenerateEdges(tess: GluTesselator) {\n // Remove zero-length edges, and contours with fewer than 3 vertices\n let e, eNext, eLnext;\n let eHead = tess.mesh.eHead;\n\n for (e = eHead.next; e !== eHead; e = eNext) {\n eNext = e.next;\n eLnext = e.Lnext;\n\n if (vertEq(e.Org, e.Sym.Org) && e.Lnext.Lnext !== e) {\n // Zero-length edge, contour has at least 3 edges\n spliceMergeVertices(tess, eLnext, e); // deletes e->Org\n tess.mesh.delete(e); // e is a self-loop\n e = eLnext;\n eLnext = e.Lnext;\n }\n if (eLnext.Lnext === e) {\n // Degenerate contour (one or two edges)\n if (eLnext !== e) {\n if (eLnext === eNext || eLnext === eNext.Sym) {\n eNext = eNext.next;\n }\n tess.mesh.delete(eLnext);\n }\n if (e === eNext || e === eNext.Sym) {\n eNext = eNext.next;\n }\n tess.mesh.delete(e);\n }\n }\n}\n\nfunction initPriorityQ(tess: GluTesselator) {\n // Insert all vertices into the priority queue which determines the\n // order in which vertices cross the sweep line.\n //\n let pq;\n let v, vHead;\n\n let vertexCount = tess.mesh.vertexCount + 8;\n\n if (tess.pq) {\n tess.pq.reset(vertexCount);\n pq = tess.pq;\n } else {\n pq = tess.pq = new PriorityQ(vertexCount);\n }\n\n vHead = tess.mesh.vHead;\n for (v = vHead.next; v !== vHead; v = v.next) {\n v.pqHandle = pq.insert(v);\n }\n\n if (v !== vHead) {\n return false;\n }\n\n pq.init();\n\n return true;\n}\n\nfunction donePriorityQ(_tess: GluTesselator) {\n // PQ is kept alive for reuse across calls\n}\n\nfunction removeDegenerateFaces(tess: GluTesselator, mesh: Mesh) {\n // Delete any degenerate faces with only two edges. WalkDirtyRegions()\n // will catch almost all of these, but it won't catch degenerate faces\n // produced by splice operations on already-processed edges.\n // The two places this can happen are in FinishLeftRegions(), when\n // we splice in a \"temporary\" edge produced by ConnectRightVertex(),\n // and in CheckForLeftSplice(), where we splice already-processed\n // edges to ensure that our dictionary invariants are not violated\n // by numerical errors.\n //\n // In both these cases it is *very* dangerous to delete the offending\n // edge at the time, since one of the routines further up the stack\n // will sometimes be keeping a pointer to that edge.\n //\n let f, fNext;\n let e;\n\n for (f = mesh.fHead.next; f !== mesh.fHead; f = fNext) {\n fNext = f.next;\n e = f.anEdge;\n\n if (e.Lnext.Lnext === e) {\n // A face with only two edges\n addWinding(e.Onext, e);\n tess.mesh.delete(e);\n }\n }\n return true;\n}\n\nexport function computeInterior(tess: GluTesselator, validate: boolean = true) {\n // tessComputeInterior( tess ) computes the planar arrangement specified\n // by the given contours, and further subdivides this arrangement\n // into regions. Each region is marked \"inside\" if it belongs\n // to the polygon, according to the rule given by tess->windingRule.\n // Each interior region is guaranteed be monotone.\n //\n let v, vNext;\n\n // Each vertex defines an event for our sweep line. Start by inserting\n // all the vertices in a priority queue. Events are processed in\n // lexicographic order, ie.\n //\n // e1 < e2 iff e1.x < e2.x || (e1.x == e2.x && e1.y < e2.y)\n removeDegenerateEdges(tess);\n\n // if error\n if (!initPriorityQ(tess)) {\n return false;\n }\n\n initEdgeDict(tess);\n\n while ((v = tess.pq.extractMin()) !== null) {\n while (true) {\n vNext = tess.pq.min();\n if (vNext === null || !vertEq(vNext, v)) break;\n\n // Merge together all vertices at exactly the same location.\n // This is more efficient than processing them one at a time,\n // simplifies the code (see ConnectLeftDegenerate), and is also\n // important for correct handling of certain degenerate cases.\n // For example, suppose there are two identical edges A and B\n // that belong to different contours (so without this code they would\n // be processed by separate sweep events). Suppose another edge C\n // crosses A and B from above. When A is processed, we split it\n // at its intersection point with C. However this also splits C,\n // so when we insert B we may compute a slightly different\n // intersection point. This might leave two edges with a small\n // gap between them. This kind of error is especially obvious\n // when using boundary extraction (BOUNDARY_CONTOURS).\n vNext = tess.pq.extractMin();\n spliceMergeVertices(tess, v.anEdge, vNext!.anEdge);\n }\n sweepEvent(tess, v);\n }\n\n tess.event = tess.dict.min().eUp.Org;\n\n doneEdgeDict(tess);\n donePriorityQ(tess);\n\n if (!removeDegenerateFaces(tess, tess.mesh)) {\n return false;\n }\n\n if (validate) {\n tess.mesh.check();\n }\n\n return true;\n}\n","// Normal vector computation and projection utilities\n\nimport { V3 } from './types';\nimport { Vertex, Mesh, HalfEdge } from './Mesh';\n\n// Compute polygon normal from vertices\nexport function computeNormal(mesh: Mesh, norm: V3): void {\n const vHead = mesh.vHead;\n let v: Vertex = vHead.next;\n\n let minVal0 = v.coords[0],\n minVal1 = v.coords[1],\n minVal2 = v.coords[2];\n let maxVal0 = minVal0,\n maxVal1 = minVal1,\n maxVal2 = minVal2;\n let minVert0: Vertex = v,\n minVert1: Vertex = v,\n minVert2: Vertex = v;\n let maxVert0: Vertex = v,\n maxVert1: Vertex = v,\n maxVert2: Vertex = v;\n\n for (v = vHead.next; v !== vHead; v = v.next) {\n const c0 = v.coords[0],\n c1 = v.coords[1],\n c2 = v.coords[2];\n if (c0 < minVal0) {\n minVal0 = c0;\n minVert0 = v;\n }\n if (c0 > maxVal0) {\n maxVal0 = c0;\n maxVert0 = v;\n }\n if (c1 < minVal1) {\n minVal1 = c1;\n minVert1 = v;\n }\n if (c1 > maxVal1) {\n maxVal1 = c1;\n maxVert1 = v;\n }\n if (c2 < minVal2) {\n minVal2 = c2;\n minVert2 = v;\n }\n if (c2 > maxVal2) {\n maxVal2 = c2;\n maxVert2 = v;\n }\n }\n\n // Find two vertices separated by at least 1/sqrt(3) of the maximum\n // distance between any two vertices\n let bestAxis = 0;\n let bestSpan = maxVal0 - minVal0;\n const span1 = maxVal1 - minVal1;\n const span2 = maxVal2 - minVal2;\n if (span1 > bestSpan) {\n bestAxis = 1;\n bestSpan = span1;\n }\n if (span2 > bestSpan) {\n bestAxis = 2;\n }\n\n let v1: Vertex, v2: Vertex;\n if (bestAxis === 0) {\n if (minVal0 >= maxVal0) {\n norm[0] = 0;\n norm[1] = 0;\n norm[2] = 1;\n return;\n }\n v1 = minVert0;\n v2 = maxVert0;\n } else if (bestAxis === 1) {\n if (minVal1 >= maxVal1) {\n norm[0] = 0;\n norm[1] = 0;\n norm[2] = 1;\n return;\n }\n v1 = minVert1;\n v2 = maxVert1;\n } else {\n if (minVal2 >= maxVal2) {\n norm[0] = 0;\n norm[1] = 0;\n norm[2] = 1;\n return;\n }\n v1 = minVert2;\n v2 = maxVert2;\n }\n\n // Find the third vertex that maximizes triangle area\n // (length of cross product == twice the triangle area)\n const d1x = v1.coords[0] - v2.coords[0];\n const d1y = v1.coords[1] - v2.coords[1];\n const d1z = v1.coords[2] - v2.coords[2];\n let maxLen2 = 0;\n\n for (v = vHead.next; v !== vHead; v = v.next) {\n const d2x = v.coords[0] - v2.coords[0];\n const d2y = v.coords[1] - v2.coords[1];\n const d2z = v.coords[2] - v2.coords[2];\n\n const tnx = d1y * d2z - d1z * d2y;\n const tny = d1z * d2x - d1x * d2z;\n const tnz = d1x * d2y - d1y * d2x;\n const tLen2 = tnx * tnx + tny * tny + tnz * tnz;\n\n if (tLen2 > maxLen2) {\n maxLen2 = tLen2;\n norm[0] = tnx;\n norm[1] = tny;\n norm[2] = tnz;\n }\n }\n\n if (maxLen2 <= 0) {\n // All points collinear -- pick any perpendicular\n norm[0] = norm[1] = norm[2] = 0;\n if (Math.abs(d1y) > Math.abs(d1x)) {\n norm[Math.abs(d1z) > Math.abs(d1y) ? 2 : 1] = 1;\n } else {\n norm[Math.abs(d1z) > Math.abs(d1x) ? 2 : 0] = 1;\n }\n }\n}\n\n// Check and fix polygon orientation\nexport function checkOrientation(mesh: Mesh, tUnit: V3): void {\n let f,\n fHead = mesh.fHead;\n let v,\n vHead = mesh.vHead;\n let e: HalfEdge;\n let area = 0;\n\n for (f = fHead.next; f !== fHead; f = f.next) {\n e = f.anEdge;\n if (e.winding <= 0) continue;\n\n do {\n area += (e.Org.s - e.Sym.Org.s) * (e.Org.t + e.Sym.Org.t);\n e = e.Lnext;\n } while (e !== f.anEdge);\n }\n\n if (area < 0) {\n for (v = vHead.next; v !== vHead; v = v.next) {\n v.t = -v.t;\n }\n tUnit[0] = -tUnit[0];\n tUnit[1] = -tUnit[1];\n tUnit[2] = -tUnit[2];\n }\n}\n\nexport function longAxis(v: V3): number {\n let i = 0;\n\n if (Math.abs(v[1]) > Math.abs(v[0])) {\n i = 1;\n }\n\n if (Math.abs(v[2]) > Math.abs(v[i])) {\n i = 2;\n }\n\n return i;\n}\n","// Callback-based rendering (OpenGL-compatible)\n//\n// Veach's original render.c optimizes for triangle fans/strips.\n// libtess.js simplified to GL_TRIANGLES only; we follow that for compatibility\n\nimport { Mesh, Face, HalfEdge, Vertex } from './Mesh';\nimport { vertLeq } from './Geom';\nimport { TessCallbacks } from './types';\n\nconst GL_TRIANGLES = 4;\nconst GL_LINE_LOOP = 2;\n\n// Fused monotone triangulation + rendering\n//\n// Walks every interior (monotone) face, triangulates with a stack algorithm\n// (Garey et al. / de Berg et al.), and emits triangles directly through\n// callbacks. The mesh is never modified -- no connect() calls, no allocations\n\n// Scratch arrays reused across all faces and calls\nlet _verts: Vertex[] = [];\nlet _chain: Int8Array = new Int8Array(64);\nlet _merged: Int32Array = new Int32Array(64);\nlet _stack: Int32Array = new Int32Array(64);\n\nfunction ensureTyped(n: number) {\n if (n > _chain.length) {\n const sz = n * 2;\n _chain = new Int8Array(sz);\n _merged = new Int32Array(sz);\n _stack = new Int32Array(sz);\n }\n}\n\nexport function renderMonotoneDirect(tess: TessCallbacks, mesh: Mesh): void {\n let began = false;\n\n for (let f: Face | null = mesh.fHead.next; f !== mesh.fHead; f = f!.next) {\n if (!f!.inside) continue;\n\n if (!began) {\n tess.callBeginCallback(GL_TRIANGLES);\n began = true;\n }\n\n emitMonotoneFace(f!, tess);\n }\n\n if (began) {\n tess.callEndCallback();\n }\n}\n\nfunction emitTri(tess: TessCallbacks, a: Vertex, b: Vertex, c: Vertex) {\n // Ensure CCW winding via signed area\n const cross = a.s * (b.t - c.t) + b.s * (c.t - a.t) + c.s * (a.t - b.t);\n if (cross >= 0) {\n tess.callVertexCallback(a.data);\n tess.callVertexCallback(b.data);\n tess.callVertexCallback(c.data);\n } else {\n tess.callVertexCallback(a.data);\n tess.callVertexCallback(c.data);\n tess.callVertexCallback(b.data);\n }\n}\n\nfunction emitMonotoneFace(face: Face, tess: TessCallbacks): void {\n // Collect boundary vertices\n let n = 0;\n let e = face.anEdge;\n do {\n _verts[n++] = e.Org;\n e = e.Lnext;\n } while (e !== face.anEdge);\n\n if (n < 3) return;\n\n if (n === 3) {\n emitTri(tess, _verts[0], _verts[1], _verts[2]);\n return;\n }\n\n ensureTyped(n);\n\n // Find rightmost and leftmost vertices\n let rightIdx = 0;\n let leftIdx = 0;\n for (let i = 1; i < n; i++) {\n if (!vertLeq(_verts[i], _verts[rightIdx])) rightIdx = i;\n if (vertLeq(_verts[i], _verts[leftIdx])) leftIdx = i;\n }\n if (rightIdx === leftIdx) return;\n\n // Build merged right-to-left sequence with chain labels\n // Upper chain: forward (rightIdx -> leftIdx)\n // Lower chain: backward (rightIdx -> leftIdx)\n let m = 0;\n _merged[m] = rightIdx;\n _chain[m] = 1;\n m++;\n\n let ui = (rightIdx + 1) % n;\n let li = (rightIdx + n - 1) % n;\n\n while (ui !== leftIdx || li !== leftIdx) {\n let takeUpper: boolean;\n if (ui === leftIdx) {\n takeUpper = false;\n } else if (li === leftIdx) {\n takeUpper = true;\n } else {\n takeUpper = !vertLeq(_verts[ui], _verts[li]);\n }\n\n if (takeUpper) {\n _merged[m] = ui;\n _chain[m] = 1;\n m++;\n ui = (ui + 1) % n;\n } else {\n _merged[m] = li;\n _chain[m] = 0;\n m++;\n li = (li + n - 1) % n;\n }\n }\n\n _merged[m] = leftIdx;\n _chain[m] = 1;\n m++;\n\n // Stack-based monotone triangulation\n let sp = 0;\n _stack[sp++] = 0;\n _stack[sp++] = 1;\n\n for (let j = 2; j < m - 1; j++) {\n if (_chain[j] !== _chain[_stack[sp - 1]]) {\n // Different chain: pop all, emit fan\n while (sp > 1) {\n const v = _stack[--sp];\n emitTri(tess, _verts[_merged[j]], _verts[_merged[v]], _verts[_merged[_stack[sp - 1]]]);\n }\n --sp;\n _stack[sp++] = j - 1;\n _stack[sp++] = j;\n } else {\n // Same chain: pop while diagonal is inside polygon\n let last = _stack[--sp];\n while (sp > 0) {\n const a = _verts[_merged[j]];\n const b = _verts[_merged[last]];\n const c = _verts[_merged[_stack[sp - 1]]];\n const cross = a.s * (b.t - c.t) + b.s * (c.t - a.t) + c.s * (a.t - b.t);\n const valid = _chain[j] === 1 ? cross <= 0 : cross >= 0;\n if (!valid) break;\n\n emitTri(tess, a, b, c);\n last = _stack[--sp];\n }\n _stack[sp++] = last;\n _stack[sp++] = j;\n }\n }\n\n // Last vertex (leftmost) connects to remaining stack\n while (sp > 1) {\n const v = _stack[--sp];\n emitTri(tess, _verts[_merged[m - 1]], _verts[_merged[v]], _verts[_merged[_stack[sp - 1]]]);\n }\n}\n\n// Set winding numbers on edges for boundary extraction\nexport function setWindingNumber(mesh: Mesh, value: number, keepOnlyBoundary: boolean): void {\n let eNext: HalfEdge | null;\n\n for (let e: HalfEdge | null = mesh.eHead.next; e !== mesh.eHead; e = eNext) {\n eNext = e!.next;\n if (e!.Sym!.Lface!.inside !== e!.Lface!.inside) {\n // Boundary edge (one side interior, one exterior)\n e!.winding = e!.Lface!.inside ? value : -value;\n } else {\n if (!keepOnlyBoundary) {\n e!.winding = 0;\n } else {\n mesh.delete(e!);\n }\n }\n }\n}\n\n// Walk mesh faces and invoke callbacks for each triangle\nexport function renderMesh(tess: TessCallbacks, mesh: Mesh, flagEdges: boolean): void {\n let beginCallbackCalled = false;\n let edgeState = -1;\n\n for (let f: Face | null = mesh.fHead.prev; f !== mesh.fHead; f = f!.prev) {\n if (!f!.inside) continue;\n\n if (!beginCallbackCalled) {\n tess.callBeginCallback(GL_TRIANGLES);\n beginCallbackCalled = true;\n }\n\n let e: HalfEdge | null = f!.anEdge;\n do {\n if (flagEdges) {\n const newState = !e!.Sym || !e!.Sym.Lface || !e!.Sym.Lface.inside ? 1 : 0;\n if (edgeState !== newState) {\n edgeState = newState;\n tess.callEdgeFlagCallback(!!edgeState);\n }\n }\n\n tess.callVertexCallback(e!.Org!.data);\n e = e!.Lnext;\n } while (e !== f!.anEdge);\n }\n\n if (beginCallbackCalled) {\n tess.callEndCallback();\n }\n}\n\n// Output boundary contours as LINE_LOOPs\nexport function renderBoundary(tess: TessCallbacks, mesh: Mesh): void {\n for (let f: Face | null = mesh.fHead.next; f !== mesh.fHead; f = f!.next) {\n if (!f!.inside) continue;\n\n tess.callBeginCallback(GL_LINE_LOOP);\n\n let e: HalfEdge | null = f!.anEdge;\n do {\n tess.callVertexCallback(e!.Org!.data);\n e = e!.Lnext;\n } while (e !== f!.anEdge);\n\n tess.callEndCallback();\n }\n}\n","export type V3 = [number, number, number | 0];\nexport type V2 = [number, number];\n\nexport type CombineCallback = (\n coords: [number, number, number],\n data: [unknown, unknown, unknown, unknown],\n weights: [number, number, number, number],\n polygonData?: unknown\n) => unknown;\n\nexport enum WINDING {\n ODD = 0,\n NONZERO = 1,\n POSITIVE = 2,\n NEGATIVE = 3,\n ABS_GEQ_TWO = 4\n}\n\nexport enum ELEMENT {\n POLYGONS = 0,\n CONNECTED_POLYGONS = 1,\n BOUNDARY_CONTOURS = 2\n}\n\n// OpenGL callback types (for gluTessCallback)\nexport enum GLU_TESS {\n BEGIN = 100100,\n EDGE_FLAG = 100104,\n VERTEX = 100101,\n END = 100102,\n ERROR = 100103,\n COMBINE = 100105,\n BEGIN_DATA = 100106,\n EDGE_FLAG_DATA = 100110,\n VERTEX_DATA = 100107,\n END_DATA = 100108,\n ERROR_DATA = 100109,\n COMBINE_DATA = 100111,\n\n WINDING_RULE = 100140,\n BOUNDARY_ONLY = 100141,\n TOLERANCE = 100142\n}\n\n// Error codes passed to the GLU_TESS.ERROR callback\nexport enum GLU_TESS_ERROR {\n MISSING_BEGIN_POLYGON = 100151,\n MISSING_BEGIN_CONTOUR = 100152,\n MISSING_END_POLYGON = 100153,\n MISSING_END_CONTOUR = 100154,\n COORD_TOO_LARGE = 100155,\n NEED_COMBINE_CALLBACK = 100156\n}\n\nexport const GL_TRIANGLES = 4;\nexport const GL_LINE_LOOP = 2;\n\n// Minimal interface for the callback methods Render.ts needs from\n// GluTesselator, avoiding a circular import\nexport interface TessCallbacks {\n callBeginCallback(type: number): void;\n callVertexCallback(data: unknown): void;\n callEndCallback(): void;\n callEdgeFlagCallback(flag: boolean): void;\n}\n","// Half-edge mesh data structure\n\nimport { assert, DEBUG } from './Assert';\nimport { V3 } from './types';\n\n// DictNode doubles as the sweep-line active region, which avoids\n// the node->key->region indirection chain\nexport class DictNode {\n next!: DictNode;\n prev!: DictNode;\n eUp: HalfEdge = null!; // null on sentinel head; always set on active regions\n windingNumber: number = 0;\n inside: boolean = false;\n sentinel: boolean = false;\n dirty: boolean = false;\n fixUpperEdge: boolean = false;\n}\n\nexport class HalfEdge {\n next!: HalfEdge;\n Org!: Vertex;\n Sym!: HalfEdge;\n Onext!: HalfEdge;\n Lnext!: HalfEdge;\n Lface!: Face;\n activeRegion: DictNode | null = null;\n winding: number = 0;\n}\n\nexport class Vertex {\n next!: Vertex;\n prev!: Vertex;\n anEdge!: HalfEdge;\n coords: V3 = [0, 0, 0];\n s: number = 0.0;\n t: number = 0.0;\n pqHandle: number = 0;\n data: unknown = null;\n}\n\nexport class Face {\n next!: Face;\n prev!: Face;\n anEdge!: HalfEdge;\n inside: boolean = false;\n}\n\n// The mesh operations below have three motivations: completeness,\n// convenience, and efficiency. The basic mesh operations are makeEdge,\n// splice, and delete. All the other edge operations can be implemented\n// in terms of these. The other operations are provided for convenience\n// and/or efficiency\n//\n// When a face is split or a vertex is added, they are inserted into the\n// global list *before* the existing vertex or face (ie. e.Org or e.Lface).\n// This makes it easier to process all vertices or faces in the global lists\n// without worrying about processing the same data twice. As a convenience,\n// when a face is split, the \"inside\" flag is copied from the old face.\n// Other internal data (v.data, v.activeRegion, f.data, f.marked,\n// f.trail, e.winding) is set to zero\n//\n// makeEdge() creates one edge, two vertices, and a loop (face).\n// The loop consists of the two new half-edges\n//\n// splice(eOrg, eDst) is the basic operation for changing the\n// mesh connectivity and topology. It changes the mesh so that\n// eOrg.Onext <- OLD(eDst.Onext)\n// eDst.Onext <- OLD(eOrg.Onext)\n// where OLD(...) means the value before the splice operation\n//\n// This can have two effects on the vertex structure:\n// - if eOrg.Org != eDst.Org, the two vertices are merged together\n// - if eOrg.Org == eDst.Org, the origin is split into two vertices\n// In both cases, eDst.Org is changed and eOrg.Org is untouched\n//\n// Similarly (and independently) for the face structure:\n// - if eOrg.Lface == eDst.Lface, one loop is split into two\n// - if eOrg.Lface != eDst.Lface, two distinct loops are joined into one\n// In both cases, eDst.Lface is changed and eOrg.Lface is unaffected\n//\n// delete(eDel) removes the edge eDel. There are several cases:\n// if (eDel.Lface != eDel.Sym.Lface), we join two loops into one; the loop\n// eDel.Lface is deleted. Otherwise, we are splitting one loop into two;\n// the newly created loop will contain eDel.Sym.Org. If the deletion of eDel\n// would create isolated vertices, those are deleted as well\n//\n// addEdgeVertex(eOrg) creates a new edge eNew such that\n// eNew == eOrg.Lnext, and eNew.Sym.Org is a newly created vertex.\n// eOrg and eNew will have the same left face\n//\n// splitEdge(eOrg) splits eOrg into two edges eOrg and eNew,\n// such that eNew == eOrg.Lnext. The new vertex is eOrg.Sym.Org == eNew.Org.\n// eOrg and eNew will have the same left face\n//\n// connect(eOrg, eDst) creates a new edge from eOrg.Sym.Org\n// to eDst.Org, and returns the corresponding half-edge eNew.\n// If eOrg.Lface == eDst.Lface, this splits one loop into two,\n// and the newly created loop is eNew.Lface. Otherwise, two disjoint\n// loops are merged into one, and the loop eDst.Lface is destroyed\n//\n// zapFace(fZap) destroys a face and removes it from the\n// global face list. All edges of fZap will have a null pointer as their\n// left face. Any edges which also have a null pointer as their right face\n// are deleted entirely (along with any isolated vertices this produces).\n// An entire mesh can be deleted by zapping its faces, one at a time,\n// in any order. Zapped faces cannot be used in further mesh operations\nexport class Mesh {\n vHead: Vertex;\n fHead: Face;\n eHead: HalfEdge;\n eHeadSym: HalfEdge;\n vertexCount: number = 0;\n\n constructor() {\n const v = new Vertex();\n const f = new Face();\n const e = new HalfEdge();\n const eSym = new HalfEdge();\n\n v.next = v.prev = v;\n\n f.next = f.prev = f;\n\n e.next = e;\n e.Sym = eSym;\n\n eSym.next = eSym;\n eSym.Sym = e;\n\n this.vHead = v;\n this.fHead = f;\n this.eHead = e;\n this.eHeadSym = eSym;\n }\n\n // Create a new pair of half-edges which form their own loop\n private makeEdgeInternal(eNext: HalfEdge) {\n const e = new HalfEdge();\n const eSym = new HalfEdge();\n\n // Insert in circular doubly-linked list before eNext\n // (prev pointer is stored in Sym.next)\n const ePrev = eNext.Sym.next;\n eSym.next = ePrev;\n ePrev.Sym.next = e;\n e.next = eNext;\n eNext.Sym.next = eSym;\n\n e.Sym = eSym;\n e.Onext = e;\n e.Lnext = eSym;\n e.winding = 0;\n e.activeRegion = null;\n\n eSym.Sym = e;\n eSym.Onext = eSym;\n eSym.Lnext = e;\n eSym.winding = 0;\n eSym.activeRegion = null;\n\n return e;\n }\n\n // Exchange a.Onext and b.Onext (see Guibas/Stolfi paper)\n private spliceInternal(a: HalfEdge, b: HalfEdge) {\n const aOnext = a.Onext;\n const bOnext = b.Onext;\n aOnext.Sym.Lnext = b;\n bOnext.Sym.Lnext = a;\n a.Onext = bOnext;\n b.Onext = aOnext;\n }\n\n // Attach a new vertex as the origin of all edges in eOrig's vertex loop\n private makeVertex(newVertex: Vertex, eOrig: HalfEdge, vNext: Vertex) {\n const vNew = newVertex;\n\n const vPrev = vNext.prev;\n vNew.prev = vPrev;\n vPrev.next = vNew;\n vNew.next = vNext;\n vNext.prev = vNew;\n\n vNew.anEdge = eOrig;\n ++this.vertexCount;\n\n let e = eOrig;\n do {\n e.Org = vNew;\n e = e.Onext;\n } while (e !== eOrig);\n }\n\n // Attach a new face as the left face of all edges in eOrig's face loop\n private makeFace(newFace: Face, eOrig: HalfEdge, fNext: Face) {\n const fNew = newFace;\n\n const fPrev = fNext.prev;\n fNew.prev = fPrev;\n fPrev.next = fNew;\n fNew.next = fNext;\n fNext.prev = fNew;\n\n fNew.anEdge = eOrig;\n fNew.inside = fNext.inside;\n\n let e = eOrig;\n do {\n e.Lface = fNew;\n e = e.Lnext;\n } while (e !== eOrig);\n }\n\n // Destroy an edge (both half-edges) and remove from the global edge list\n private killEdge(eDel: HalfEdge) {\n const eNext = eDel.next;\n const ePrev = eDel.Sym.next;\n eNext.Sym.next = ePrev;\n ePrev.Sym.next = eNext;\n }\n\n // Destroy a vertex and update its edge loop to point to newOrg\n // newOrg is null when the vertex is being destroyed (edges are about to be killed)\n private killVertex(vDel: Vertex, newOrg: Vertex | null) {\n const eStart = vDel.anEdge;\n let e = eStart;\n do {\n e.Org = newOrg!;\n e = e.Onext;\n } while (e !== eStart);\n\n const vPrev = vDel.prev;\n const vNext = vDel.next;\n vNext.prev = vPrev;\n vPrev.next = vNext;\n --this.vertexCount;\n }\n\n // newLface is null when the face is being destroyed (edges are about to be killed)\n private killFace(fDel: Face, newLface: Face | null) {\n const eStart = fDel.anEdge;\n\n let e = eStart;\n do {\n e.Lface = newLface!;\n e = e.Lnext;\n } while (e !== eStart);\n\n const fPrev = fDel.prev;\n const fNext = fDel.next;\n fNext.prev = fPrev;\n fPrev.next = fNext;\n }\n\n makeEdge() {\n const newVertex1 = new Vertex();\n const newVertex2 = new Vertex();\n const newFace = new Face();\n const e = this.makeEdgeInternal(this.eHead);\n this.makeVertex(newVertex1, e, this.vHead);\n this.makeVertex(newVertex2, e.Sym, this.vHead);\n this.makeFace(newFace, e, this.fHead);\n return e;\n }\n\n splice(eOrg: HalfEdge, eDst: HalfEdge) {\n let joiningLoops = false;\n let joiningVertices = false;\n\n if (eOrg === eDst) return;\n\n if (eDst.Org !== eOrg.Org) {\n joiningVertices = true;\n this.killVertex(eDst.Org, eOrg.Org);\n }\n if (eDst.Lface !== eOrg.Lface) {\n joiningLoops = true;\n this.killFace(eDst.Lface, eOrg.Lface);\n }\n\n this.spliceInternal(eDst, eOrg);\n\n if (!joiningVertices) {\n const newVertex = new Vertex();\n this.makeVertex(newVertex, eDst, eOrg.Org);\n eOrg.Org.anEdge = eOrg;\n }\n if (!joiningLoops) {\n const newFace = new Face();\n this.makeFace(newFace, eDst, eOrg.Lface);\n eOrg.Lface.anEdge = eOrg;\n }\n }\n\n delete(eDel: HalfEdge) {\n const eDelSym = eDel.Sym;\n let joiningLoops = false;\n\n if (eDel.Lface !== eDel.Sym.Lface) {\n joiningLoops = true;\n this.killFace(eDel.Lface, eDel.Sym.Lface);\n }\n\n if (eDel.Onext === eDel) {\n this.killVertex(eDel.Org, null);\n } else {\n eDel.Sym.Lface.anEdge = eDel.Sym.Lnext;\n eDel.Org.anEdge = eDel.Onext;\n\n this.spliceInternal(eDel, eDel.Sym.Lnext);\n if (!joiningLoops) {\n const newFace = new Face();\n this.makeFace(newFace, eDel, eDel.Lface);\n }\n }\n\n if (eDelSym.Onext === eDelSym) {\n this.killVertex(eDelSym.Org, null);\n this.killFace(eDelSym.Lface, null);\n } else {\n eDel.Lface.anEdge = eDelSym.Sym.Lnext;\n eDelSym.Org.anEdge = eDelSym.Onext;\n this.spliceInternal(eDelSym, eDelSym.Sym.Lnext);\n }\n\n this.killEdge(eDel);\n }\n\n addEdgeVertex(eOrg: HalfEdge) {\n const eNew = this.makeEdgeInternal(eOrg);\n const eNewSym = eNew.Sym;\n\n this.spliceInternal(eNew, eOrg.Lnext);\n\n eNew.Org = eOrg.Sym.Org;\n\n const newVertex = new Vertex();\n this.makeVertex(newVertex, eNewSym, eNew.Org);\n\n eNew.Lface = eNewSym.Lface = eOrg.Lface;\n\n return eNew;\n }\n\n splitEdge(eOrg: HalfEdge) {\n const tempHalfEdge = this.addEdgeVertex(eOrg);\n const eNew = tempHalfEdge.Sym;\n\n this.spliceInternal(eOrg.Sym, eOrg.Sym.Sym.Lnext);\n this.spliceInternal(eOrg.Sym, eNew);\n\n eOrg.Sym.Org = eNew.Org;\n eNew.Sym.Org.anEdge = eNew.Sym;\n eNew.Sym.Lface = eOrg.Sym.Lface;\n eNew.winding = eOrg.winding;\n eNew.Sym.winding = eOrg.Sym.winding;\n\n return eNew;\n }\n\n connect(eOrg: HalfEdge, eDst: HalfEdge) {\n let joiningLoops = false;\n const eNew = this.makeEdgeInternal(eOrg);\n const eNewSym = eNew.Sym;\n\n if (eDst.Lface !== eOrg.Lface) {\n joiningLoops = true;\n this.killFace(eDst.Lface, eOrg.Lface);\n }\n\n this.spliceInternal(eNew, eOrg.Lnext);\n this.spliceInternal(eNewSym, eDst);\n\n eNew.Org = eOrg.Sym.Org;\n eNewSym.Org = eDst.Org;\n eNew.Lface = eNewSym.Lface = eOrg.Lface;\n\n eOrg.Lface.anEdge = eNewSym;\n\n if (!joiningLoops) {\n const newFace = new Face();\n this.makeFace(newFace, eNew, eOrg.Lface);\n }\n return eNew;\n }\n\n zapFace(fZap: Face) {\n const eStart = fZap.anEdge;\n let e, eNext, eSym;\n let fPrev, fNext;\n\n eNext = eStart.Lnext;\n do {\n e = eNext;\n eNext = e.Lnext;\n\n e.Lface = null!;\n if (!(e.Sym.Lface as Face | null)) {\n if (e.Onext === e) {\n this.killVertex(e.Org, null);\n } else {\n e.Org.anEdge = e.Onext;\n this.spliceInternal(e, e.Sym.Lnext);\n }\n eSym = e.Sym;\n if (eSym.Onext === eSym) {\n this.killVertex(eSym.Org, null);\n } else {\n eSym.Org.anEdge = eSym.Onext;\n this.spliceInternal(eSym, eSym.Sym.Lnext);\n }\n this.killEdge(e);\n }\n } while (e != eStart);\n\n fPrev = fZap.prev;\n fNext = fZap.next;\n fNext.prev = fPrev;\n fPrev.next = fNext;\n }\n\n countFaceVerts(f: Face) {\n let eCur = f.anEdge;\n let n = 0;\n do {\n n++;\n eCur = eCur.Lnext;\n } while (eCur !== f.anEdge);\n return n;\n }\n\n // Mesh self-consistency check (only runs when DEBUG is true)\n check() {\n if (!DEBUG) return;\n\n const fHead = this.fHead;\n const vHead = this.vHead;\n const eHead = this.eHead;\n let f, fPrev, v, vPrev, e, ePrev;\n\n fPrev = fHead;\n for (fPrev = fHead; (f = fPrev.next) !== fHead; fPrev = f) {\n assert(f.prev === fPrev);\n e = f.anEdge;\n do {\n assert(e.Sym !== e);\n assert(e.Sym.Sym === e);\n assert(e.Lnext.Onext.Sym === e);\n assert(e.Onext.Sym.Lnext === e);\n assert(e.Lface === f);\n e = e.Lnext;\n } while (e !== f.anEdge);\n }\n assert(f.prev === fPrev && !f.anEdge); // sentinel face has no edge\n\n vPrev = vHead;\n for (vPrev = vHead; (v = vPrev.next) !== vHead; vPrev = v) {\n assert(v.prev === vPrev);\n e = v.anEdge;\n do {\n assert(e.Sym !== e);\n assert(e.Sym.Sym === e);\n assert(e.Lnext.Onext.Sym === e);\n assert(e.Onext.Sym.Lnext === e);\n assert(e.Org === v);\n e = e.Onext;\n } while (e !== v.anEdge);\n }\n assert(v.prev === vPrev && !v.anEdge); // sentinel vertex has no edge\n\n ePrev = eHead;\n for (ePrev = eHead; (e = ePrev.next) !== eHead; ePrev = e) {\n assert(e.Sym.next === ePrev.Sym);\n assert(e.Sym !== e);\n assert(e.Sym.Sym === e);\n assert(e.Org);\n assert(e.Sym.Org);\n assert(e.Lnext.Onext.Sym === e);\n assert(e.Onext.Sym.Lnext === e);\n }\n assert(e.Sym.next === ePrev.Sym && e.Sym === this.eHeadSym && e.Sym.Sym === e && !e.Org); // sentinel\n }\n}\n","// Binary heap for dynamic priority queue operations, using flat typed\n// arrays for node/handle indirection\n\nimport { assert } from './Assert';\nimport { Vertex } from './Mesh';\n\nexport class PriorityQHeap {\n max: number = 0;\n nodeHandles: Int32Array;\n handleKeys: Array<Vertex | null>;\n handleNodes: Int32Array;\n initialized: boolean = false;\n freeList: number = 0;\n size: number = 0;\n\n constructor(size: number) {\n this.max = size;\n\n this.nodeHandles = new Int32Array(size + 1);\n this.handleKeys = new Array<Vertex | null>(size + 1).fill(null);\n this.handleNodes = new Int32Array(size + 1);\n\n this.initialized = false;\n\n // So that min() returns null on empty heap\n this.nodeHandles[1] = 1;\n this.handleKeys[1] = null;\n }\n\n // Reset for reuse on warm calls (grows arrays if needed, nulls old\n // vertex refs so the previous mesh can be collected)\n reset(size: number) {\n if (size + 1 > this.max) {\n this.max = size;\n this.nodeHandles = new Int32Array(size + 1);\n this.handleKeys = new Array<Vertex | null>(size + 1).fill(null);\n this.handleNodes = new Int32Array(size + 1);\n } else {\n const keys = this.handleKeys;\n for (let i = 1; i <= this.size; i++) keys[i] = null;\n }\n this.size = 0;\n this.freeList = 0;\n this.initialized = false;\n this.nodeHandles[1] = 1;\n this.handleKeys[1] = null;\n }\n\n floatDown(curr: number) {\n const nodeHandles = this.nodeHandles;\n const handleKeys = this.handleKeys;\n const handleNodes = this.handleNodes;\n let hCurr = nodeHandles[curr];\n while (true) {\n let child = curr << 1;\n if (child > this.size) break;\n\n let minChild = child;\n let hChild = nodeHandles[child];\n if (child + 1 <= this.size) {\n const hRight = nodeHandles[child + 1];\n const kRight = handleKeys[hRight]!;\n const kChild = handleKeys[hChild]!;\n if (kRight.s < kChild.s || (kRight.s === kChild.s && kRight.t <= kChild.t)) {\n minChild = child + 1;\n hChild = hRight;\n }\n }\n\n const kCurr = handleKeys[hCurr]!;\n const kMin = handleKeys[hChild]!;\n if (kCurr.s < kMin.s || (kCurr.s === kMin.s && kCurr.t <= kMin.t)) break;\n\n nodeHandles[curr] = hChild;\n handleNodes[hChild] = curr;\n curr = minChild;\n }\n nodeHandles[curr] = hCurr;\n handleNodes[hCurr] = curr;\n }\n\n floatUp(curr: number) {\n const nodeHandles = this.nodeHandles;\n const handleKeys = this.handleKeys;\n const handleNodes = this.handleNodes;\n let hCurr = nodeHandles[curr];\n while (true) {\n const parent = curr >> 1;\n if (parent === 0) break;\n const hParent = nodeHandles[parent];\n const kParent = handleKeys[hParent]!;\n const kCurr = handleKeys[hCurr]!;\n if (kParent.s < kCurr.s || (kParent.s === kCurr.s && kParent.t <= kCurr.t)) break;\n\n nodeHandles[curr] = hParent;\n handleNodes[hParent] = curr;\n curr = parent;\n }\n nodeHandles[curr] = hCurr;\n handleNodes[hCurr] = curr;\n }\n\n // O(n) heap construction (vs O(n log n) for repeated insert)\n init() {\n for (let i = this.size >> 1; i >= 1; --i) {\n this.floatDown(i);\n }\n this.initialized = true;\n }\n\n isEmpty(): boolean {\n return this.size === 0;\n }\n\n min(): Vertex | null {\n if (this.size === 0) return null;\n return this.handleKeys[this.nodeHandles[1]];\n }\n\n insert(keyNew: Vertex) {\n let curr;\n let free;\n\n curr = ++this.size;\n if (curr * 2 > this.max) {\n this.max *= 2;\n const newNodeHandles = new Int32Array(this.max + 1);\n const newHandleNodes = new Int32Array(this.max + 1);\n const newHandleKeys = new Array<Vertex | null>(this.max + 1).fill(null);\n\n newNodeHandles.set(this.nodeHandles);\n newHandleNodes.set(this.handleNodes);\n for (let i = 0; i < this.handleKeys.length; i++) {\n newHandleKeys[i] = this.handleKeys[i];\n }\n\n this.nodeHandles = newNodeHandles;\n this.handleNodes = newHandleNodes;\n this.handleKeys = newHandleKeys;\n }\n\n if (this.freeList === 0) {\n free = curr;\n } else {\n free = this.freeList;\n this.freeList = this.handleNodes[free];\n }\n\n this.nodeHandles[curr] = free;\n this.handleNodes[free] = curr;\n this.handleKeys[free] = keyNew;\n\n if (this.initialized) {\n this.floatUp(curr);\n }\n return free;\n }\n\n extractMin() {\n const nodeHandles = this.nodeHandles;\n const handleKeys = this.handleKeys;\n const handleNodes = this.handleNodes;\n let hMin = nodeHandles[1];\n let min = handleKeys[hMin];\n\n if (this.size > 0) {\n nodeHandles[1] = nodeHandles[this.size];\n handleNodes[nodeHandles[1]] = 1;\n\n handleKeys[hMin] = null;\n handleNodes[hMin] = this.freeList;\n this.freeList = hMin;\n\n --this.size;\n if (this.size > 0) {\n this.floatDown(1);\n }\n }\n return min;\n }\n\n delete(hCurr: number) {\n const nodeHandles = this.nodeHandles;\n const handleKeys = this.handleKeys;\n const handleNodes = this.handleNodes;\n let curr;\n\n assert(hCurr >= 1 && hCurr <= this.max && handleKeys[hCurr] !== null);\n\n curr = handleNodes[hCurr];\n nodeHandles[curr] = nodeHandles[this.size];\n handleNodes[nodeHandles[curr]] = curr;\n\n --this.size;\n if (curr <= this.size) {\n if (curr <= 1) {\n this.floatDown(curr);\n } else {\n const parent = curr >> 1;\n const kParent = handleKeys[nodeHandles[parent]]!;\n const kCurr = handleKeys[nodeHandles[curr]]!;\n if (kParent.s < kCurr.s || (kParent.s === kCurr.s && kParent.t <= kCurr.t)) {\n this.floatDown(curr);\n } else {\n this.floatUp(curr);\n }\n }\n }\n handleKeys[hCurr] = null;\n handleNodes[hCurr] = this.freeList;\n this.freeList = hCurr;\n }\n}\n","// Priority queue of vertices, ordered by vertLeq. Combines a sorted\n// array (for initial vertices) with a heap (for intersections\n// discovered during the sweep)\n\nimport { Vertex } from './Mesh';\nimport { PriorityQHeap } from './PriorityQHeap';\nimport { vertLeq } from './Geom';\n\n// Below this vertex count, use heap-only mode to avoid the overhead\n// of sorted-array allocation + Array.sort for small polygons\nconst HEAP_ONLY_THRESHOLD = 128;\n\nexport class PriorityQ {\n private heap: PriorityQHeap;\n private keys!: Array<Vertex>;\n private order: Array<number> | null = null;\n private size: number = 0;\n private max: number = 0;\n private initialized: boolean = false;\n private heapOnly: boolean;\n\n constructor(size: number) {\n this.max = size;\n this.size = 0;\n this.initialized = false;\n this.heapOnly = size <= HEAP_ONLY_THRESHOLD;\n\n this.heap = new PriorityQHeap(size);\n\n if (!this.heapOnly) {\n this.keys = new Array<Vertex>(size).fill(null as any);\n }\n }\n\n reset(size: number) {\n this.heap.reset(size);\n this.heapOnly = size <= HEAP_ONLY_THRESHOLD;\n if (!this.heapOnly) {\n if (!this.keys || size > this.max) {\n this.keys = new Array<Vertex>(size).fill(null as any);\n }\n }\n if (size > this.max) this.max = size;\n this.size = 0;\n this.initialized = false;\n this.order = null;\n }\n\n insert(keyNew: Vertex): number {\n if (this.heapOnly || this.initialized) {\n return this.heap.insert(keyNew);\n }\n\n const curr = this.size;\n if (++this.size >= this.max) {\n const oldMax = this.max;\n this.max *= 2;\n const newKeys = new Array<Vertex>(this.max).fill(null as any);\n for (let i = 0; i < oldMax; i++) {\n newKeys[i] = this.keys[i];\n }\n this.keys = newKeys;\n }\n\n this.keys[curr] = keyNew;\n\n // Negative handles index into the sorted array\n return -(curr + 1);\n }\n\n // Sort keys and initialize the heap. O(n log n) for sorted+heap mode,\n // O(n) heapify for heap-only mode\n init(): boolean {\n if (this.heapOnly) {\n this.initialized = true;\n this.heap.init();\n return true;\n }\n\n this.order = new Array(this.size);\n\n for (let i = 0; i < this.size; i++) {\n this.order[i] = i;\n }\n\n // Sort indirect pointers in descending order so extractMin pops from the end\n const keys = this.keys;\n this.order.sort((a, b) => {\n const va = keys[a];\n const vb = keys[b];\n if (va.s < vb.s) return 1;\n if (va.s > vb.s) return -1;\n return va.t <= vb.t ? 1 : -1;\n });\n\n this.max = this.size;\n this.initialized = true;\n this.heap.init();\n\n return true;\n }\n\n extractMin(): Vertex | null {\n if (this.heapOnly || this.size === 0) {\n return this.heap.extractMin();\n }\n\n const sortMin = this.keys[this.order![this.size - 1]];\n if (!this.heap.isEmpty()) {\n const heapMin = this.heap.min();\n if (heapMin && vertLeq(heapMin, sortMin)) {\n return this.heap.extractMin();\n }\n }\n\n do {\n --this.size;\n } while (this.size > 0 && this.keys[this.order![this.size - 1]] === null);\n\n return sortMin;\n }\n\n min(): Vertex | null {\n if (this.heapOnly || this.size === 0) {\n return this.heap.min();\n }\n\n const sortMin = this.keys[this.order![this.size - 1]];\n if (!this.heap.isEmpty()) {\n const heapMin = this.heap.min();\n if (heapMin && vertLeq(heapMin, sortMin)) {\n return heapMin;\n }\n }\n\n return sortMin;\n }\n\n delete(handle: number): void {\n if (handle >= 0) {\n this.heap.delete(handle);\n } else {\n const curr = -(handle + 1);\n this.keys[curr] = null as any;\n }\n }\n\n isEmpty(): boolean {\n if (this.heapOnly) return this.heap.isEmpty();\n return this.size === 0 && this.heap.isEmpty();\n }\n}\n","// Monotone region tessellation\n\nimport { Mesh, Face, HalfEdge } from './Mesh';\nimport { vertLeq, edgeGoesLeft, edgeGoesRight, edgeSign } from './Geom';\n\nexport class TessMono {\n static addWinding(eDst: HalfEdge, eSrc: HalfEdge): void {\n eDst.winding += eSrc.winding;\n eDst.Sym.winding += eSrc.Sym.winding;\n }\n\n // Tessellate a monotone region (single CCW loop of half-edges) into\n // triangles. \"Monotone\" means any vertical line intersects the interior\n // in a single interval.\n //\n // The algorithm (Preparata and Shamos) maintains two edge chains, upper\n // and lower, processing vertices right to left. After each vertex, the\n // untessellated region has one chain that is a single edge and another\n // that is concave. Each step adds the rightmost unprocessed vertex to\n // one chain and fans out as many triangles as possible, restoring the\n // invariant\n static tessellateMonoRegion(mesh: Mesh, face: Face): boolean {\n let up: HalfEdge, lo: HalfEdge;\n\n up = face.anEdge;\n if (!(up.Lnext !== up && up.Lnext.Lnext !== up)) {\n throw new Error('Monotone region has degenerate topology');\n }\n\n for (; vertLeq(up.Sym.Org, up.Org); up = up.Onext.Sym);\n for (; vertLeq(up.Org, up.Sym.Org); up = up.Lnext);\n\n lo = up.Onext.Sym;\n\n let tempHalfEdge!: HalfEdge;\n\n while (up.Lnext !== lo) {\n if (vertLeq(up.Sym.Org, lo.Org)) {\n // up.Sym.Org is on the left -- safe to fan triangles from lo.Org\n while (lo.Lnext !== up && (edgeGoesLeft(lo.Lnext) || edgeSign(lo.Org, lo.Sym.Org, lo.Lnext.Sym.Org) <= 0.0)) {\n tempHalfEdge = mesh.connect(lo.Lnext, lo);\n lo = tempHalfEdge.Sym;\n }\n lo = lo.Onext.Sym;\n } else {\n // lo.Org is on the left -- fan triangles from up.Sym.Org\n while (\n lo.Lnext !== up &&\n (edgeGoesRight(up.Onext.Sym) || edgeSign(up.Sym.Org, up.Org, up.Onext.Sym.Org) >= 0.0)\n ) {\n tempHalfEdge = mesh.connect(up, up.Onext.Sym);\n up = tempHalfEdge.Sym;\n }\n up = up.Lnext;\n }\n }\n\n // lo.Org == up.Sym.Org == the leftmost vertex; fan the remainder\n if (lo.Lnext === up) {\n throw new Error('Monotone region has insufficient vertices');\n }\n\n while (lo.Lnext.Lnext !== up) {\n tempHalfEdge = mesh.connect(lo.Lnext, lo);\n lo = tempHalfEdge.Sym;\n }\n\n return true;\n }\n\n // Tessellate all interior (monotone) regions\n static tessellateInterior(mesh: Mesh): boolean {\n let next: Face;\n\n for (let f = mesh.fHead.next; f !== mesh.fHead; f = next) {\n next = f!.next!;\n\n if (f!.inside) {\n if (!TessMono.tessellateMonoRegion(mesh, f!)) {\n return false;\n }\n }\n }\n\n return true;\n }\n}\n","import { WINDING, GLU_TESS, V3, V2, CombineCallback } from './types';\nimport { Mesh, HalfEdge, Vertex } from './Mesh';\nimport { computeInterior } from './Sweep';\nimport * as Normal from './Normal';\nimport { TessMono } from './TessMono';\nimport { PriorityQ } from './PriorityQ';\nimport { Dict } from './Dict';\nimport { DEBUG } from './Assert';\nimport { renderMesh, renderBoundary as renderBoundaryMesh, setWindingNumber, renderMonotoneDirect } from './Render';\n\n// The begin/end calls must be properly nested\nenum TessState {\n T_DORMANT,\n T_IN_POLYGON,\n T_IN_CONTOUR\n}\n\nexport class GluTesselator {\n private state: TessState = TessState.T_DORMANT;\n private lastEdge: HalfEdge | null = null;\n private hasNonZeroZ: boolean = false;\n\n // When the normal is known at vertex time (2D, Z-axis normal),\n // s/t are set inline so projectPolygon can skip its vertex loop\n private projDone: boolean = false;\n private projTMul: number = 1;\n private projMinS: number = 0;\n private projMaxS: number = 0;\n private projMinT: number = 0;\n private projMaxT: number = 0;\n\n mesh!: Mesh;\n normal_: V3 = [0, 0, 0];\n sUnit!: V3;\n tUnit!: V3;\n bmin!: V2;\n bmax!: V2;\n windingRule_ = WINDING.ODD;\n windingRuleRaw_: number = WINDING.ODD;\n dict!: Dict;\n pq!: PriorityQ;\n event!: Vertex;\n\n private beginCallback?: (type: number, polygonData?: unknown) => void;\n private vertexCallback?: (data: unknown, polygonData?: unknown) => void;\n private endCallback?: (polygonData?: unknown) => void;\n private edgeFlagCallback?: (flag: boolean, polygonData?: unknown) => void;\n private errorCallback?: (errorNumber: number) => void;\n private errorDataCallback?: (errorNumber: number, polygonData?: unknown) => void;\n onCombine_?: CombineCallback;\n meshCallback_?: (mesh: Mesh) => void;\n polygonData: unknown = null;\n noEmit_ = false;\n\n gluTessCallback(which: GLU_TESS.BEGIN | GLU_TESS.BEGIN_DATA, fn?: (type: number, polygonData?: unknown) => void): void;\n gluTessCallback(which: GLU_TESS.VERTEX | GLU_TESS.VERTEX_DATA, fn?: (data: unknown, polygonData?: unknown) => void): void;\n gluTessCallback(which: GLU_TESS.END | GLU_TESS.END_DATA, fn?: (polygonData?: unknown) => void): void;\n gluTessCallback(which: GLU_TESS.ERROR, fn?: (errorNumber: number) => void): void;\n gluTessCallback(which: GLU_TESS.ERROR_DATA, fn?: (errorNumber: number, polygonData?: unknown) => void): void;\n gluTessCallback(which: GLU_TESS.EDGE_FLAG | GLU_TESS.EDGE_FLAG_DATA, fn?: (flag: boolean, polygonData?: unknown) => void): void;\n gluTessCallback(which: GLU_TESS.COMBINE | GLU_TESS.COMBINE_DATA, fn?: CombineCallback): void;\n gluTessCallback(which: number, fn?: (...args: any[]) => any): void;\n gluTessCallback(which: number, fn?: (...args: any[]) => any): void {\n const callback = fn || null;\n\n switch (which) {\n case 100100: // GLU_TESS_BEGIN\n case 100106: // GLU_TESS_BEGIN_DATA\n this.beginCallback = callback as typeof this.beginCallback;\n break;\n case 100104: // GLU_TESS_EDGE_FLAG\n case 100110: // GLU_TESS_EDGE_FLAG_DATA\n this.edgeFlagCallback = callback as typeof this.edgeFlagCallback;\n this.flagBoundary = true;\n break;\n case 100101: // GLU_TESS_VERTEX\n case 100107: // GLU_TESS_VERTEX_DATA\n this.vertexCallback = callback as typeof this.vertexCallback;\n break;\n case 100102: // GLU_TESS_END\n case 100108: // GLU_TESS_END_DATA\n this.endCallback = callback as typeof this.endCallback;\n break;\n case 100103: // GLU_TESS_ERROR\n this.errorCallback = callback as typeof this.errorCallback;\n break;\n case 100109: // GLU_TESS_ERROR_DATA\n this.errorDataCallback = callback as typeof this.errorDataCallback;\n break;\n case 100105: // GLU_TESS_COMBINE\n case 100111: // GLU_TESS_COMBINE_DATA\n this.onCombine_ = callback as typeof this.onCombine_;\n break;\n case 100112: // GLU_TESS_MESH\n this.meshCallback_ = callback as typeof this.meshCallback_;\n break;\n default:\n throw new Error('GLU_INVALID_ENUM');\n }\n }\n\n gluTessProperty(which: number, value: number | boolean): void {\n switch (which) {\n case 100140: {\n // GLU_TESS_WINDING_RULE\n // Accept both native (0-4) and libtess.js-style (100130-100134) values\n const v = value as number;\n const normalized = v >= 100130 ? v - 100130 : v;\n if (normalized < 0 || normalized > 4) {\n throw new Error('GLU_INVALID_VALUE');\n }\n this.windingRuleRaw_ = v;\n this.windingRule_ = normalized;\n break;\n }\n case 100141: // GLU_TESS_BOUNDARY_ONLY\n this.boundaryOnly_ = !!value;\n break;\n case 100142: // GLU_TESS_TOLERANCE\n // Accepted but ignored\n break;\n default:\n throw new Error('GLU_INVALID_ENUM');\n }\n }\n\n gluGetTessProperty(which: number): number | boolean {\n switch (which) {\n case 100140: // GLU_TESS_WINDING_RULE\n return this.windingRuleRaw_;\n case 100141: // GLU_TESS_BOUNDARY_ONLY\n return this.boundaryOnly_;\n case 100142: // GLU_TESS_TOLERANCE\n return 0;\n default:\n throw new Error('GLU_INVALID_ENUM');\n }\n }\n\n gluTessNormal(x: number, y: number, z: number): void {\n this.normal_[0] = x;\n this.normal_[1] = y;\n this.normal_[2] = z;\n if (z !== 0 && !x && !y) {\n this.projTMul = z > 0 ? 1 : -1;\n }\n }\n\n callBeginCallback(type: number): void {\n if (this.beginCallback) {\n this.beginCallback(type, this.polygonData);\n }\n }\n\n callVertexCallback(data: unknown): void {\n if (this.vertexCallback) {\n this.vertexCallback(data, this.polygonData);\n }\n }\n\n callEndCallback(): void {\n if (this.endCallback) {\n this.endCallback(this.polygonData);\n }\n }\n\n callEdgeFlagCallback(flag: boolean): void {\n if (this.edgeFlagCallback) {\n this.edgeFlagCallback(flag, this.polygonData);\n }\n }\n\n callErrorOrErrorData(errorNumber: number): void {\n if (this.errorDataCallback) {\n this.errorDataCallback(errorNumber, this.polygonData);\n } else if (this.errorCallback) {\n this.errorCallback(errorNumber);\n }\n }\n\n // GLU error recovery: walk state to target, auto-inserting missing calls\n private requireState(target: TessState): void {\n if (this.state === target) return;\n while (this.state !== target) {\n if (this.state < target) {\n if (this.state === TessState.T_DORMANT) {\n this.callErrorOrErrorData(100151); // GLU_TESS_MISSING_BEGIN_POLYGON\n this.gluTessBeginPolygon();\n } else if (this.state === TessState.T_IN_POLYGON) {\n this.callErrorOrErrorData(100152); // GLU_TESS_MISSING_BEGIN_CONTOUR\n this.gluTessBeginContour();\n }\n } else {\n if (this.state === TessState.T_IN_CONTOUR) {\n this.callErrorOrErrorData(100154); // GLU_TESS_MISSING_END_CONTOUR\n this.gluTessEndContour();\n } else if (this.state === TessState.T_IN_POLYGON) {\n this.callErrorOrErrorData(100153); // GLU_TESS_MISSING_END_POLYGON\n this.gluTessEndPolygon();\n }\n }\n }\n }\n\n boundaryOnly_: boolean = false;\n flagBoundary: boolean = false;\n\n // Determine the polygon normal and project vertices onto the sweep plane\n private projectPolygon() {\n const mesh = this.mesh!;\n const vHead = mesh.vHead;\n const nx = this.normal_[0];\n const ny = this.normal_[1];\n const nz = this.normal_[2];\n let sUnit, tUnit;\n\n if (!this.sUnit) this.sUnit = [0, 0, 0];\n if (!this.tUnit) this.tUnit = [0, 0, 0];\n if (!this.bmin) this.bmin = [0, 0];\n if (!this.bmax) this.bmax = [0, 0];\n\n sUnit = this.sUnit;\n tUnit = this.tUnit;\n\n if (this.projDone) {\n sUnit[0] = 1.0;\n sUnit[1] = 0.0;\n sUnit[2] = 0.0;\n const tMul = nz > 0 ? 1 : -1;\n tUnit[0] = 0.0;\n tUnit[1] = tMul;\n tUnit[2] = 0.0;\n this.bmin[0] = this.projMinS;\n this.bmin[1] = this.projMinT;\n this.bmax[0] = this.projMaxS;\n this.bmax[1] = this.projMaxT;\n return;\n }\n\n // Fast path for 2D input (all z=0, normal is z-aligned or auto)\n if (!this.hasNonZeroZ && !nx && !ny) {\n sUnit[0] = 1.0;\n sUnit[1] = 0.0;\n sUnit[2] = 0.0;\n\n // Determine t-axis sign: use explicit normal when available, otherwise\n // compute the normal (one O(n) pass) for the sign\n let tMul: number;\n let needOrientationCheck = false;\n if (nz) {\n tMul = nz > 0 ? 1 : -1;\n } else {\n const norm: V3 = [0, 0, 0];\n Normal.computeNormal(mesh, norm);\n tMul = norm[2] > 0 ? 1 : -1;\n needOrientationCheck = true;\n }\n tUnit[0] = 0.0;\n tUnit[1] = tMul;\n tUnit[2] = 0.0;\n\n let v = vHead.next!;\n let s = v.coords[0];\n let t = v.coords[1] * tMul;\n v.s = s;\n v.t = t;\n let minS = s,\n maxS = s,\n minT = t,\n maxT = t;\n for (v = v.next!; v !== vHead; v = v!.next!) {\n s = v.coords[0];\n t = v.coords[1] * tMul;\n v.s = s;\n v.t = t;\n if (s < minS) minS = s;\n else if (s > maxS) maxS = s;\n if (t < minT) minT = t;\n else if (t > maxT) maxT = t;\n }\n\n this.bmin[0] = minS;\n this.bmax[0] = maxS;\n\n if (needOrientationCheck) {\n Normal.checkOrientation(mesh, this.tUnit);\n // checkOrientation may have negated all t values; if so, the\n // t bounds flipped sign and swapped min/max\n if (tUnit[1] !== tMul) {\n this.bmin[1] = -maxT;\n this.bmax[1] = -minT;\n } else {\n this.bmin[1] = minT;\n this.bmax[1] = maxT;\n }\n } else {\n this.bmin[1] = minT;\n this.bmax[1] = maxT;\n }\n return;\n }\n\n // 3D / explicit-normal path\n let computedNormal = false;\n const norm: V3 = [nx, ny, nz];\n if (!nx && !ny && !nz) {\n Normal.computeNormal(mesh, norm);\n computedNormal = true;\n }\n\n const axis = Normal.longAxis(norm);\n\n // Project perpendicular to a coordinate axis -- better numerically\n sUnit[axis] = 0;\n sUnit[(axis + 1) % 3] = 1.0;\n sUnit[(axis + 2) % 3] = 0.0;\n\n tUnit[axis] = 0;\n tUnit[(axis + 1) % 3] = 0.0;\n tUnit[(axis + 2) % 3] = norm[axis] > 0 ? 1.0 : -1.0;\n\n // Project the vertices onto the sweep plane and compute bounds\n let v = vHead.next!;\n v.s = v.coords[0] * sUnit[0] + v.coords[1] * sUnit[1] + v.coords[2] * sUnit[2];\n v.t = v.coords[0] * tUnit[0] + v.coords[1] * tUnit[1] + v.coords[2] * tUnit[2];\n let minS = v.s,\n maxS = v.s,\n minT = v.t,\n maxT = v.t;\n for (v = v.next!; v !== vHead; v = v!.next!) {\n const s = v.coords[0] * sUnit[0] + v.coords[1] * sUnit[1] + v.coords[2] * sUnit[2];\n const t = v.coords[0] * tUnit[0] + v.coords[1] * tUnit[1] + v.coords[2] * tUnit[2];\n v.s = s;\n v.t = t;\n if (s < minS) minS = s;\n else if (s > maxS) maxS = s;\n if (t < minT) minT = t;\n else if (t > maxT) maxT = t;\n }\n\n if (computedNormal) {\n Normal.checkOrientation(mesh, this.tUnit);\n }\n\n // Bounds must reflect post-orientation-check values. checkOrientation\n // negates all t values when it flips, so detect that and fix bounds\n if (computedNormal && tUnit[(axis + 2) % 3] !== (norm[axis] > 0 ? 1.0 : -1.0)) {\n this.bmin[0] = minS;\n this.bmax[0] = maxS;\n this.bmin[1] = -maxT;\n this.bmax[1] = -minT;\n } else {\n this.bmin[0] = minS;\n this.bmax[0] = maxS;\n this.bmin[1] = minT;\n this.bmax[1] = maxT;\n }\n }\n\n gluTessBeginPolygon(data?: unknown): void {\n this.requireState(TessState.T_DORMANT);\n\n this.state = TessState.T_IN_POLYGON;\n this.noEmit_ = false;\n this.mesh = new Mesh();\n this.hasNonZeroZ = false;\n const nz = this.normal_[2];\n this.projDone = nz !== 0 && !this.normal_[0] && !this.normal_[1];\n this.projMinS = Infinity;\n this.projMaxS = -Infinity;\n this.projMinT = Infinity;\n this.projMaxT = -Infinity;\n this.polygonData = data;\n }\n\n gluTessBeginContour(): void {\n this.requireState(TessState.T_IN_POLYGON);\n\n this.state = TessState.T_IN_CONTOUR;\n this.lastEdge = null;\n }\n\n gluTessVertex(coords: [number, number] | [number, number, number], data?: unknown): void {\n this.requireState(TessState.T_IN_CONTOUR);\n\n // Clamp to GLU_TESS_MAX_COORD\n let x = coords[0];\n let y = coords[1];\n let z = coords.length > 2 ? (coords[2] as number) : 0;\n let tooLarge = false;\n if (x < -1e150) { x = -1e150; tooLarge = true; }\n else if (x > 1e150) { x = 1e150; tooLarge = true; }\n if (y < -1e150) { y = -1e150; tooLarge = true; }\n else if (y > 1e150) { y = 1e150; tooLarge = true; }\n if (z < -1e150) { z = -1e150; tooLarge = true; }\n else if (z > 1e150) { z = 1e150; tooLarge = true; }\n if (tooLarge) {\n this.callErrorOrErrorData(100155); // GLU_TESS_COORD_TOO_LARGE\n }\n\n let e = this.lastEdge;\n\n if (e === null) {\n // Make a self-loop (one vertex, one edge)\n e = this.mesh.makeEdge();\n this.mesh.splice(e, e.Sym);\n } else {\n // Create a new vertex and edge which immediately follow e\n // in the ordering around the left face\n this.mesh.splitEdge(e);\n e = e.Lnext!;\n }\n\n // The new vertex is now e->Org\n e.Org.data = data || null;\n e.Org.coords[0] = x;\n e.Org.coords[1] = y;\n if (z !== 0) {\n e.Org.coords[2] = z;\n this.hasNonZeroZ = true;\n this.projDone = false; // can't use 2D fast-path\n } else {\n e.Org.coords[2] = 0.0;\n }\n\n if (this.projDone) {\n e.Org.s = x;\n const t = y * this.projTMul;\n e.Org.t = t;\n if (x < this.projMinS) this.projMinS = x;\n if (x > this.projMaxS) this.projMaxS = x;\n if (t < this.projMinT) this.projMinT = t;\n if (t > this.projMaxT) this.projMaxT = t;\n }\n\n // The winding of an edge says how the winding number changes as we\n // cross from the edge's right face to its left face. We add the\n // vertices in such an order that a CCW contour will add +1 to\n // the winding number of the region inside the contour\n e.winding = 1;\n e.Sym.winding = -1;\n\n this.lastEdge = e;\n }\n\n gluTessEndContour(): void {\n this.requireState(TessState.T_IN_CONTOUR);\n this.state = TessState.T_IN_POLYGON;\n }\n\n gluTessEndPolygon(): void {\n this.requireState(TessState.T_IN_POLYGON);\n\n this.state = TessState.T_DORMANT;\n\n this.compute(this.windingRule_, undefined, DEBUG);\n\n const mesh = this.mesh!;\n\n if (!this.noEmit_) {\n if (this.boundaryOnly_) {\n setWindingNumber(mesh, 1, true);\n renderBoundaryMesh(this, mesh);\n } else if (this.flagBoundary) {\n TessMono.tessellateInterior(mesh);\n renderMesh(this, mesh, true);\n } else {\n renderMonotoneDirect(this, mesh);\n }\n }\n\n if (this.meshCallback_) {\n this.meshCallback_(mesh);\n }\n\n this.mesh = null!;\n this.lastEdge = null;\n this.event = null!;\n this.polygonData = null;\n this.dict = null!;\n }\n\n gluDeleteTess(): void {\n this.requireState(TessState.T_DORMANT);\n }\n\n // Run the sweep-line algorithm, keeping the mesh for further queries\n compute(windingRule: WINDING = WINDING.ODD, normal?: V3, validate: boolean = DEBUG): void {\n if (this.state !== TessState.T_DORMANT) {\n if (this.state === TessState.T_IN_POLYGON) {\n this.state = TessState.T_DORMANT;\n }\n }\n\n if (!this.mesh) this.mesh = new Mesh();\n\n if (normal) {\n this.normal_[0] = normal[0];\n this.normal_[1] = normal[1];\n this.normal_[2] = normal[2];\n }\n\n this.windingRule_ = windingRule;\n\n this.projectPolygon();\n computeInterior(this, validate);\n }\n\n // Destructive -- deletes interior edges and merges faces into boundary\n // loops. If you need both triangles and boundaries, call renderTriangles\n // first\n renderBoundary(): void {\n if (!this.mesh) return;\n setWindingNumber(this.mesh, 1, true);\n renderBoundaryMesh(this, this.mesh);\n }\n\n // Tessellate interior and emit triangles\n renderTriangles(flagEdges: boolean = false): void {\n if (!this.mesh) return;\n if (flagEdges) {\n TessMono.tessellateInterior(this.mesh);\n renderMesh(this, this.mesh, true);\n } else {\n renderMonotoneDirect(this, this.mesh);\n }\n }\n}\n","// Assertions are eliminated by the build process when DEBUG is false\nexport const DEBUG = false;\n\nexport function assert(cond: any, message?: string): void {\n if (DEBUG) {\n if (!cond) {\n throw new Error(message || 'Assertion Failed!');\n }\n }\n}\n"],"names":["vertEq","u","v","s","t","vertLeq","transLeq","edgeGoesLeft","e","Sym","Org","edgeGoesRight","vertL1dist","Math","abs","edgeEval","w","gapL","gapR","edgeSign","transEval","transSign","interpolate","a","x","b","y","edgeLeq","tess","reg1","reg2","ev","event","e1","eUp","e2","regionBelow","r","prev","regionAbove","next","addWinding","eDst","eSrc","winding","deleteRegion","reg","activeRegion","dict","delete","fixUpperEdge","newEdge","mesh","topLeftRegion","org","connect","Lnext","topRightRegion","dst","addRegionBelow","regAbove","eNewUp","regNew","DictNode","insertBefore","sentinel","dirty","isWindingInside","n","windingRule_","WINDING","ODD","NONZERO","POSITIVE","NEGATIVE","ABS_GEQ_TWO","Error","finishRegion","f","Lface","inside","anEdge","finishLeftRegions","regFirst","regLast","regPrev","ePrev","Onext","splice","addRightEdges","regUp","eFirst","eLast","eTopLeft","cleanUp","firstTime","windingNumber","checkForRightSplice","walkDirtyRegions","callCombine","isect","data","weights","needed","onCombine_","scratchCoords","coords","polygonData","callErrorOrErrorData","noEmit_","spliceMergeVertices","scratchData","scratchWeights","vertexWeights","weightIndex","t1","t2","w0","w1","undefined","regLo","eLo","pq","pqHandle","splitEdge","checkForLeftSplice","checkForIntersect","orgUp","orgLo","tMinUp","tMaxLo","dstUp","dstLo","isectScratch","Vertex","orgMin","min","max","o1","d1","o2","d2","z1","z2","intersect","insert","getIntersectData","connectLeftVertex","vEvent","eNew","tmp","tmpRegionScratch","search","computeWinding","sweepEvent","eTopRight","connectLeftDegenerate","eBottomLeft","degenerate","connectRightVertex","addSentinel","smin","smax","makeEdge","computeNormal","norm","vHead","minVal0","minVal1","minVal2","maxVal0","maxVal1","maxVal2","minVert0","minVert1","minVert2","maxVert0","maxVert1","maxVert2","c0","c1","c2","bestAxis","bestSpan","span1","v1","v2","d1x","d1y","d1z","maxLen2","d2x","d2y","d2z","tnx","tny","tnz","tLen2","checkOrientation","tUnit","fHead","area","renderMonotoneDirect","began","callBeginCallback","emitMonotoneFace","callEndCallback","emitTri","c","callVertexCallback","face","_verts","_chain","length","sz","Int8Array","_merged","Int32Array","_stack","ensureTyped","rightIdx","leftIdx","i","m","ui","li","takeUpper","sp","j","last","cross","setWindingNumber","value","eNext","eHead","renderMesh","beginCallbackCalled","edgeState","newState","callEdgeFlagCallback","renderBoundary","ELEMENT","GLU_TESS","GLU_TESS_ERROR","GL_TRIANGLES","GL_LINE_LOOP","HalfEdge","Face","Mesh","eHeadSym","vertexCount","constructor","eSym","this","makeEdgeInternal","spliceInternal","aOnext","bOnext","makeVertex","newVertex","eOrig","vNext","vNew","vPrev","makeFace","newFace","fNext","fNew","fPrev","killEdge","eDel","killVertex","vDel","newOrg","eStart","killFace","fDel","newLface","newVertex1","newVertex2","eOrg","joiningLoops","joiningVertices","eDelSym","addEdgeVertex","eNewSym","zapFace","fZap","countFaceVerts","eCur","check","PriorityQHeap","nodeHandles","handleKeys","handleNodes","initialized","freeList","size","Array","fill","reset","keys","floatDown","curr","hCurr","child","minChild","hChild","hRight","kRight","kChild","kCurr","kMin","floatUp","parent","hParent","kParent","init","isEmpty","keyNew","free","newNodeHandles","newHandleNodes","newHandleKeys","set","extractMin","hMin","PriorityQ","heap","order","heapOnly","oldMax","newKeys","sort","va","vb","sortMin","heapMin","handle","Dict","head","frame","newNode","key","node","TessMono","tessellateMonoRegion","up","lo","tempHalfEdge","tessellateInterior","TessState","GluTesselator","state","T_DORMANT","lastEdge","hasNonZeroZ","projDone","projTMul","projMinS","projMaxS","projMinT","projMaxT","normal_","sUnit","bmin","bmax","windingRuleRaw_","beginCallback","vertexCallback","endCallback","edgeFlagCallback","errorCallback","errorDataCallback","meshCallback_","gluTessCallback","which","fn","callback","flagBoundary","gluTessProperty","normalized","boundaryOnly_","gluGetTessProperty","gluTessNormal","z","type","flag","errorNumber","requireState","target","gluTessBeginPolygon","T_IN_POLYGON","gluTessBeginContour","T_IN_CONTOUR","gluTessEndContour","gluTessEndPolygon","projectPolygon","nx","ny","nz","tMul","needOrientationCheck","Normal.computeNormal","minS","maxS","minT","maxT","Normal.checkOrientation","computedNormal","axis","Normal.longAxis","Infinity","gluTessVertex","tooLarge","compute","renderBoundaryMesh","gluDeleteTess","windingRule","normal","validate","eLnext","removeDegenerateEdges","initPriorityQ","h","tmax","initEdgeDict","doneEdgeDict","removeDegenerateFaces","computeInterior","renderTriangles","flagEdges"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "libtess-ts",
3
+ "version": "0.0.1",
4
+ "description": "Optimized TypeScript port of libtess.js, itself based on the GLU tessellator. Fast, high quality polygon triangulation",
5
+ "type": "module",
6
+ "main": "./dist/libtess.min.js",
7
+ "module": "./dist/libtess.min.js",
8
+ "types": "./types/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./types/index.d.ts",
12
+ "development": "./dist/libtess.es.js",
13
+ "default": "./dist/libtess.min.js"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "types",
19
+ "LICENSE",
20
+ "README.md"
21
+ ],
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "git+https://github.com/countertype/libtess-ts.git"
25
+ },
26
+ "homepage": "https://github.com/countertype/libtess-ts",
27
+ "bugs": "https://github.com/countertype/libtess-ts/issues",
28
+ "scripts": {
29
+ "build": "rollup -c && tsc --emitDeclarationOnly",
30
+ "test": "vitest run",
31
+ "test:watch": "vitest",
32
+ "bench": "node benchmarks/benchmark.mjs",
33
+ "bench:browser": "node benchmarks/browser/serve.js",
34
+ "format": "prettier --write 'src/**/*.ts' 'test/**/*.ts' '*.js' '*.ts' '*.json'",
35
+ "format:check": "prettier --check 'src/**/*.ts' 'test/**/*.ts' '*.js' '*.ts' '*.json'",
36
+ "prepublishOnly": "npm run build && npm test"
37
+ },
38
+ "keywords": [
39
+ "tessellate",
40
+ "tessellation",
41
+ "triangulate",
42
+ "triangulation",
43
+ "glu",
44
+ "libtess",
45
+ "polygon",
46
+ "geometry",
47
+ "mesh",
48
+ "typescript"
49
+ ],
50
+ "author": "Jeremy Tribby, Countertype LLC",
51
+ "license": "SGI-B-2.0",
52
+ "sideEffects": false,
53
+ "devDependencies": {
54
+ "@rollup/plugin-terser": "^0.4.4",
55
+ "@rollup/plugin-typescript": "^12.3.0",
56
+ "libtess": "^1.2.2",
57
+ "prettier": "^3.8.1",
58
+ "rollup": "^4.57.1",
59
+ "tess2-ts": "^1.0.8",
60
+ "typescript": "^5.9.3",
61
+ "vitest": "^4.0.18"
62
+ }
63
+ }
@@ -0,0 +1,2 @@
1
+ export declare const DEBUG = false;
2
+ export declare function assert(cond: any, message?: string): void;
@@ -0,0 +1,13 @@
1
+ import { DictNode } from './Mesh';
2
+ import { GluTesselator } from './GluTesselator';
3
+ export declare class Dict {
4
+ head: DictNode;
5
+ frame: GluTesselator;
6
+ constructor(frame: GluTesselator);
7
+ min(): DictNode;
8
+ max(): DictNode;
9
+ insert(newNode: DictNode): DictNode;
10
+ search(key: DictNode): DictNode;
11
+ insertBefore(node: DictNode, newNode: DictNode): DictNode;
12
+ delete(node: DictNode): void;
13
+ }
@@ -0,0 +1,13 @@
1
+ import { Vertex, HalfEdge } from './Mesh';
2
+ export declare function vertEq(u: Vertex, v: Vertex): boolean;
3
+ export declare function vertLeq(u: Vertex, v: Vertex): boolean;
4
+ export declare function transLeq(u: Vertex, v: Vertex): boolean;
5
+ export declare function edgeGoesLeft(e: HalfEdge): boolean;
6
+ export declare function edgeGoesRight(e: HalfEdge): boolean;
7
+ export declare function vertL1dist(u: Vertex, v: Vertex): number;
8
+ export declare function edgeEval(u: Vertex, v: Vertex, w: Vertex): number;
9
+ export declare function edgeSign(u: Vertex, v: Vertex, w: Vertex): number;
10
+ export declare function transEval(u: Vertex, v: Vertex, w: Vertex): number;
11
+ export declare function transSign(u: Vertex, v: Vertex, w: Vertex): number;
12
+ export declare function interpolate(a: number, x: number, b: number, y: number): number;
13
+ export declare function intersect(o1: Vertex, d1: Vertex, o2: Vertex, d2: Vertex, v: Vertex): void;
@@ -0,0 +1,65 @@
1
+ import { WINDING, GLU_TESS, V3, V2, CombineCallback } from './types';
2
+ import { Mesh, Vertex } from './Mesh';
3
+ import { PriorityQ } from './PriorityQ';
4
+ import { Dict } from './Dict';
5
+ export declare class GluTesselator {
6
+ private state;
7
+ private lastEdge;
8
+ private hasNonZeroZ;
9
+ private projDone;
10
+ private projTMul;
11
+ private projMinS;
12
+ private projMaxS;
13
+ private projMinT;
14
+ private projMaxT;
15
+ mesh: Mesh;
16
+ normal_: V3;
17
+ sUnit: V3;
18
+ tUnit: V3;
19
+ bmin: V2;
20
+ bmax: V2;
21
+ windingRule_: WINDING;
22
+ windingRuleRaw_: number;
23
+ dict: Dict;
24
+ pq: PriorityQ;
25
+ event: Vertex;
26
+ private beginCallback?;
27
+ private vertexCallback?;
28
+ private endCallback?;
29
+ private edgeFlagCallback?;
30
+ private errorCallback?;
31
+ private errorDataCallback?;
32
+ onCombine_?: CombineCallback;
33
+ meshCallback_?: (mesh: Mesh) => void;
34
+ polygonData: unknown;
35
+ noEmit_: boolean;
36
+ gluTessCallback(which: GLU_TESS.BEGIN | GLU_TESS.BEGIN_DATA, fn?: (type: number, polygonData?: unknown) => void): void;
37
+ gluTessCallback(which: GLU_TESS.VERTEX | GLU_TESS.VERTEX_DATA, fn?: (data: unknown, polygonData?: unknown) => void): void;
38
+ gluTessCallback(which: GLU_TESS.END | GLU_TESS.END_DATA, fn?: (polygonData?: unknown) => void): void;
39
+ gluTessCallback(which: GLU_TESS.ERROR, fn?: (errorNumber: number) => void): void;
40
+ gluTessCallback(which: GLU_TESS.ERROR_DATA, fn?: (errorNumber: number, polygonData?: unknown) => void): void;
41
+ gluTessCallback(which: GLU_TESS.EDGE_FLAG | GLU_TESS.EDGE_FLAG_DATA, fn?: (flag: boolean, polygonData?: unknown) => void): void;
42
+ gluTessCallback(which: GLU_TESS.COMBINE | GLU_TESS.COMBINE_DATA, fn?: CombineCallback): void;
43
+ gluTessCallback(which: number, fn?: (...args: any[]) => any): void;
44
+ gluTessProperty(which: number, value: number | boolean): void;
45
+ gluGetTessProperty(which: number): number | boolean;
46
+ gluTessNormal(x: number, y: number, z: number): void;
47
+ callBeginCallback(type: number): void;
48
+ callVertexCallback(data: unknown): void;
49
+ callEndCallback(): void;
50
+ callEdgeFlagCallback(flag: boolean): void;
51
+ callErrorOrErrorData(errorNumber: number): void;
52
+ private requireState;
53
+ boundaryOnly_: boolean;
54
+ flagBoundary: boolean;
55
+ private projectPolygon;
56
+ gluTessBeginPolygon(data?: unknown): void;
57
+ gluTessBeginContour(): void;
58
+ gluTessVertex(coords: [number, number] | [number, number, number], data?: unknown): void;
59
+ gluTessEndContour(): void;
60
+ gluTessEndPolygon(): void;
61
+ gluDeleteTess(): void;
62
+ compute(windingRule?: WINDING, normal?: V3, validate?: boolean): void;
63
+ renderBoundary(): void;
64
+ renderTriangles(flagEdges?: boolean): void;
65
+ }
@@ -0,0 +1,61 @@
1
+ import { V3 } from './types';
2
+ export declare class DictNode {
3
+ next: DictNode;
4
+ prev: DictNode;
5
+ eUp: HalfEdge;
6
+ windingNumber: number;
7
+ inside: boolean;
8
+ sentinel: boolean;
9
+ dirty: boolean;
10
+ fixUpperEdge: boolean;
11
+ }
12
+ export declare class HalfEdge {
13
+ next: HalfEdge;
14
+ Org: Vertex;
15
+ Sym: HalfEdge;
16
+ Onext: HalfEdge;
17
+ Lnext: HalfEdge;
18
+ Lface: Face;
19
+ activeRegion: DictNode | null;
20
+ winding: number;
21
+ }
22
+ export declare class Vertex {
23
+ next: Vertex;
24
+ prev: Vertex;
25
+ anEdge: HalfEdge;
26
+ coords: V3;
27
+ s: number;
28
+ t: number;
29
+ pqHandle: number;
30
+ data: unknown;
31
+ }
32
+ export declare class Face {
33
+ next: Face;
34
+ prev: Face;
35
+ anEdge: HalfEdge;
36
+ inside: boolean;
37
+ }
38
+ export declare class Mesh {
39
+ vHead: Vertex;
40
+ fHead: Face;
41
+ eHead: HalfEdge;
42
+ eHeadSym: HalfEdge;
43
+ vertexCount: number;
44
+ constructor();
45
+ private makeEdgeInternal;
46
+ private spliceInternal;
47
+ private makeVertex;
48
+ private makeFace;
49
+ private killEdge;
50
+ private killVertex;
51
+ private killFace;
52
+ makeEdge(): HalfEdge;
53
+ splice(eOrg: HalfEdge, eDst: HalfEdge): void;
54
+ delete(eDel: HalfEdge): void;
55
+ addEdgeVertex(eOrg: HalfEdge): HalfEdge;
56
+ splitEdge(eOrg: HalfEdge): HalfEdge;
57
+ connect(eOrg: HalfEdge, eDst: HalfEdge): HalfEdge;
58
+ zapFace(fZap: Face): void;
59
+ countFaceVerts(f: Face): number;
60
+ check(): void;
61
+ }