Buckets:
| ; | |
| Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); | |
| const THREE = require("three"); | |
| const COLOR_SPACE_SVG = "srgb"; | |
| const SVGLoader = /* @__PURE__ */ (() => { | |
| class SVGLoader2 extends THREE.Loader { | |
| constructor(manager) { | |
| super(manager); | |
| this.defaultDPI = 90; | |
| this.defaultUnit = "px"; | |
| } | |
| load(url, onLoad, onProgress, onError) { | |
| const scope = this; | |
| const loader = new THREE.FileLoader(scope.manager); | |
| loader.setPath(scope.path); | |
| loader.setRequestHeader(scope.requestHeader); | |
| loader.setWithCredentials(scope.withCredentials); | |
| loader.load( | |
| url, | |
| function(text) { | |
| try { | |
| onLoad(scope.parse(text)); | |
| } catch (e) { | |
| if (onError) { | |
| onError(e); | |
| } else { | |
| console.error(e); | |
| } | |
| scope.manager.itemError(url); | |
| } | |
| }, | |
| onProgress, | |
| onError | |
| ); | |
| } | |
| parse(text) { | |
| const scope = this; | |
| function parseNode(node, style) { | |
| if (node.nodeType !== 1) | |
| return; | |
| const transform = getNodeTransform(node); | |
| let isDefsNode = false; | |
| let path = null; | |
| switch (node.nodeName) { | |
| case "svg": | |
| style = parseStyle(node, style); | |
| break; | |
| case "style": | |
| parseCSSStylesheet(node); | |
| break; | |
| case "g": | |
| style = parseStyle(node, style); | |
| break; | |
| case "path": | |
| style = parseStyle(node, style); | |
| if (node.hasAttribute("d")) | |
| path = parsePathNode(node); | |
| break; | |
| case "rect": | |
| style = parseStyle(node, style); | |
| path = parseRectNode(node); | |
| break; | |
| case "polygon": | |
| style = parseStyle(node, style); | |
| path = parsePolygonNode(node); | |
| break; | |
| case "polyline": | |
| style = parseStyle(node, style); | |
| path = parsePolylineNode(node); | |
| break; | |
| case "circle": | |
| style = parseStyle(node, style); | |
| path = parseCircleNode(node); | |
| break; | |
| case "ellipse": | |
| style = parseStyle(node, style); | |
| path = parseEllipseNode(node); | |
| break; | |
| case "line": | |
| style = parseStyle(node, style); | |
| path = parseLineNode(node); | |
| break; | |
| case "defs": | |
| isDefsNode = true; | |
| break; | |
| case "use": | |
| style = parseStyle(node, style); | |
| const href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href") || ""; | |
| const usedNodeId = href.substring(1); | |
| const usedNode = node.viewportElement.getElementById(usedNodeId); | |
| if (usedNode) { | |
| parseNode(usedNode, style); | |
| } else { | |
| console.warn("SVGLoader: 'use node' references non-existent node id: " + usedNodeId); | |
| } | |
| break; | |
| } | |
| if (path) { | |
| if (style.fill !== void 0 && style.fill !== "none") { | |
| path.color.setStyle(style.fill, COLOR_SPACE_SVG); | |
| } | |
| transformPath(path, currentTransform); | |
| paths.push(path); | |
| path.userData = { node, style }; | |
| } | |
| const childNodes = node.childNodes; | |
| for (let i = 0; i < childNodes.length; i++) { | |
| const node2 = childNodes[i]; | |
| if (isDefsNode && node2.nodeName !== "style" && node2.nodeName !== "defs") { | |
| continue; | |
| } | |
| parseNode(node2, style); | |
| } | |
| if (transform) { | |
| transformStack.pop(); | |
| if (transformStack.length > 0) { | |
| currentTransform.copy(transformStack[transformStack.length - 1]); | |
| } else { | |
| currentTransform.identity(); | |
| } | |
| } | |
| } | |
| function parsePathNode(node) { | |
| const path = new THREE.ShapePath(); | |
| const point = new THREE.Vector2(); | |
| const control = new THREE.Vector2(); | |
| const firstPoint = new THREE.Vector2(); | |
| let isFirstPoint = true; | |
| let doSetFirstPoint = false; | |
| const d = node.getAttribute("d"); | |
| if (d === "" || d === "none") | |
| return null; | |
| const commands = d.match(/[a-df-z][^a-df-z]*/gi); | |
| for (let i = 0, l = commands.length; i < l; i++) { | |
| const command = commands[i]; | |
| const type = command.charAt(0); | |
| const data2 = command.slice(1).trim(); | |
| if (isFirstPoint === true) { | |
| doSetFirstPoint = true; | |
| isFirstPoint = false; | |
| } | |
| let numbers; | |
| switch (type) { | |
| case "M": | |
| numbers = parseFloats(data2); | |
| for (let j = 0, jl = numbers.length; j < jl; j += 2) { | |
| point.x = numbers[j + 0]; | |
| point.y = numbers[j + 1]; | |
| control.x = point.x; | |
| control.y = point.y; | |
| if (j === 0) { | |
| path.moveTo(point.x, point.y); | |
| } else { | |
| path.lineTo(point.x, point.y); | |
| } | |
| if (j === 0) | |
| firstPoint.copy(point); | |
| } | |
| break; | |
| case "H": | |
| numbers = parseFloats(data2); | |
| for (let j = 0, jl = numbers.length; j < jl; j++) { | |
| point.x = numbers[j]; | |
| control.x = point.x; | |
| control.y = point.y; | |
| path.lineTo(point.x, point.y); | |
| if (j === 0 && doSetFirstPoint === true) | |
| firstPoint.copy(point); | |
| } | |
| break; | |
| case "V": | |
| numbers = parseFloats(data2); | |
| for (let j = 0, jl = numbers.length; j < jl; j++) { | |
| point.y = numbers[j]; | |
| control.x = point.x; | |
| control.y = point.y; | |
| path.lineTo(point.x, point.y); | |
| if (j === 0 && doSetFirstPoint === true) | |
| firstPoint.copy(point); | |
| } | |
| break; | |
| case "L": | |
| numbers = parseFloats(data2); | |
| for (let j = 0, jl = numbers.length; j < jl; j += 2) { | |
| point.x = numbers[j + 0]; | |
| point.y = numbers[j + 1]; | |
| control.x = point.x; | |
| control.y = point.y; | |
| path.lineTo(point.x, point.y); | |
| if (j === 0 && doSetFirstPoint === true) | |
| firstPoint.copy(point); | |
| } | |
| break; | |
| case "C": | |
| numbers = parseFloats(data2); | |
| for (let j = 0, jl = numbers.length; j < jl; j += 6) { | |
| path.bezierCurveTo( | |
| numbers[j + 0], | |
| numbers[j + 1], | |
| numbers[j + 2], | |
| numbers[j + 3], | |
| numbers[j + 4], | |
| numbers[j + 5] | |
| ); | |
| control.x = numbers[j + 2]; | |
| control.y = numbers[j + 3]; | |
| point.x = numbers[j + 4]; | |
| point.y = numbers[j + 5]; | |
| if (j === 0 && doSetFirstPoint === true) | |
| firstPoint.copy(point); | |
| } | |
| break; | |
| case "S": | |
| numbers = parseFloats(data2); | |
| for (let j = 0, jl = numbers.length; j < jl; j += 4) { | |
| path.bezierCurveTo( | |
| getReflection(point.x, control.x), | |
| getReflection(point.y, control.y), | |
| numbers[j + 0], | |
| numbers[j + 1], | |
| numbers[j + 2], | |
| numbers[j + 3] | |
| ); | |
| control.x = numbers[j + 0]; | |
| control.y = numbers[j + 1]; | |
| point.x = numbers[j + 2]; | |
| point.y = numbers[j + 3]; | |
| if (j === 0 && doSetFirstPoint === true) | |
| firstPoint.copy(point); | |
| } | |
| break; | |
| case "Q": | |
| numbers = parseFloats(data2); | |
| for (let j = 0, jl = numbers.length; j < jl; j += 4) { | |
| path.quadraticCurveTo(numbers[j + 0], numbers[j + 1], numbers[j + 2], numbers[j + 3]); | |
| control.x = numbers[j + 0]; | |
| control.y = numbers[j + 1]; | |
| point.x = numbers[j + 2]; | |
| point.y = numbers[j + 3]; | |
| if (j === 0 && doSetFirstPoint === true) | |
| firstPoint.copy(point); | |
| } | |
| break; | |
| case "T": | |
| numbers = parseFloats(data2); | |
| for (let j = 0, jl = numbers.length; j < jl; j += 2) { | |
| const rx = getReflection(point.x, control.x); | |
| const ry = getReflection(point.y, control.y); | |
| path.quadraticCurveTo(rx, ry, numbers[j + 0], numbers[j + 1]); | |
| control.x = rx; | |
| control.y = ry; | |
| point.x = numbers[j + 0]; | |
| point.y = numbers[j + 1]; | |
| if (j === 0 && doSetFirstPoint === true) | |
| firstPoint.copy(point); | |
| } | |
| break; | |
| case "A": | |
| numbers = parseFloats(data2, [3, 4], 7); | |
| for (let j = 0, jl = numbers.length; j < jl; j += 7) { | |
| if (numbers[j + 5] == point.x && numbers[j + 6] == point.y) | |
| continue; | |
| const start = point.clone(); | |
| point.x = numbers[j + 5]; | |
| point.y = numbers[j + 6]; | |
| control.x = point.x; | |
| control.y = point.y; | |
| parseArcCommand( | |
| path, | |
| numbers[j], | |
| numbers[j + 1], | |
| numbers[j + 2], | |
| numbers[j + 3], | |
| numbers[j + 4], | |
| start, | |
| point | |
| ); | |
| if (j === 0 && doSetFirstPoint === true) | |
| firstPoint.copy(point); | |
| } | |
| break; | |
| case "m": | |
| numbers = parseFloats(data2); | |
| for (let j = 0, jl = numbers.length; j < jl; j += 2) { | |
| point.x += numbers[j + 0]; | |
| point.y += numbers[j + 1]; | |
| control.x = point.x; | |
| control.y = point.y; | |
| if (j === 0) { | |
| path.moveTo(point.x, point.y); | |
| } else { | |
| path.lineTo(point.x, point.y); | |
| } | |
| if (j === 0) | |
| firstPoint.copy(point); | |
| } | |
| break; | |
| case "h": | |
| numbers = parseFloats(data2); | |
| for (let j = 0, jl = numbers.length; j < jl; j++) { | |
| point.x += numbers[j]; | |
| control.x = point.x; | |
| control.y = point.y; | |
| path.lineTo(point.x, point.y); | |
| if (j === 0 && doSetFirstPoint === true) | |
| firstPoint.copy(point); | |
| } | |
| break; | |
| case "v": | |
| numbers = parseFloats(data2); | |
| for (let j = 0, jl = numbers.length; j < jl; j++) { | |
| point.y += numbers[j]; | |
| control.x = point.x; | |
| control.y = point.y; | |
| path.lineTo(point.x, point.y); | |
| if (j === 0 && doSetFirstPoint === true) | |
| firstPoint.copy(point); | |
| } | |
| break; | |
| case "l": | |
| numbers = parseFloats(data2); | |
| for (let j = 0, jl = numbers.length; j < jl; j += 2) { | |
| point.x += numbers[j + 0]; | |
| point.y += numbers[j + 1]; | |
| control.x = point.x; | |
| control.y = point.y; | |
| path.lineTo(point.x, point.y); | |
| if (j === 0 && doSetFirstPoint === true) | |
| firstPoint.copy(point); | |
| } | |
| break; | |
| case "c": | |
| numbers = parseFloats(data2); | |
| for (let j = 0, jl = numbers.length; j < jl; j += 6) { | |
| path.bezierCurveTo( | |
| point.x + numbers[j + 0], | |
| point.y + numbers[j + 1], | |
| point.x + numbers[j + 2], | |
| point.y + numbers[j + 3], | |
| point.x + numbers[j + 4], | |
| point.y + numbers[j + 5] | |
| ); | |
| control.x = point.x + numbers[j + 2]; | |
| control.y = point.y + numbers[j + 3]; | |
| point.x += numbers[j + 4]; | |
| point.y += numbers[j + 5]; | |
| if (j === 0 && doSetFirstPoint === true) | |
| firstPoint.copy(point); | |
| } | |
| break; | |
| case "s": | |
| numbers = parseFloats(data2); | |
| for (let j = 0, jl = numbers.length; j < jl; j += 4) { | |
| path.bezierCurveTo( | |
| getReflection(point.x, control.x), | |
| getReflection(point.y, control.y), | |
| point.x + numbers[j + 0], | |
| point.y + numbers[j + 1], | |
| point.x + numbers[j + 2], | |
| point.y + numbers[j + 3] | |
| ); | |
| control.x = point.x + numbers[j + 0]; | |
| control.y = point.y + numbers[j + 1]; | |
| point.x += numbers[j + 2]; | |
| point.y += numbers[j + 3]; | |
| if (j === 0 && doSetFirstPoint === true) | |
| firstPoint.copy(point); | |
| } | |
| break; | |
| case "q": | |
| numbers = parseFloats(data2); | |
| for (let j = 0, jl = numbers.length; j < jl; j += 4) { | |
| path.quadraticCurveTo( | |
| point.x + numbers[j + 0], | |
| point.y + numbers[j + 1], | |
| point.x + numbers[j + 2], | |
| point.y + numbers[j + 3] | |
| ); | |
| control.x = point.x + numbers[j + 0]; | |
| control.y = point.y + numbers[j + 1]; | |
| point.x += numbers[j + 2]; | |
| point.y += numbers[j + 3]; | |
| if (j === 0 && doSetFirstPoint === true) | |
| firstPoint.copy(point); | |
| } | |
| break; | |
| case "t": | |
| numbers = parseFloats(data2); | |
| for (let j = 0, jl = numbers.length; j < jl; j += 2) { | |
| const rx = getReflection(point.x, control.x); | |
| const ry = getReflection(point.y, control.y); | |
| path.quadraticCurveTo(rx, ry, point.x + numbers[j + 0], point.y + numbers[j + 1]); | |
| control.x = rx; | |
| control.y = ry; | |
| point.x = point.x + numbers[j + 0]; | |
| point.y = point.y + numbers[j + 1]; | |
| if (j === 0 && doSetFirstPoint === true) | |
| firstPoint.copy(point); | |
| } | |
| break; | |
| case "a": | |
| numbers = parseFloats(data2, [3, 4], 7); | |
| for (let j = 0, jl = numbers.length; j < jl; j += 7) { | |
| if (numbers[j + 5] == 0 && numbers[j + 6] == 0) | |
| continue; | |
| const start = point.clone(); | |
| point.x += numbers[j + 5]; | |
| point.y += numbers[j + 6]; | |
| control.x = point.x; | |
| control.y = point.y; | |
| parseArcCommand( | |
| path, | |
| numbers[j], | |
| numbers[j + 1], | |
| numbers[j + 2], | |
| numbers[j + 3], | |
| numbers[j + 4], | |
| start, | |
| point | |
| ); | |
| if (j === 0 && doSetFirstPoint === true) | |
| firstPoint.copy(point); | |
| } | |
| break; | |
| case "Z": | |
| case "z": | |
| path.currentPath.autoClose = true; | |
| if (path.currentPath.curves.length > 0) { | |
| point.copy(firstPoint); | |
| path.currentPath.currentPoint.copy(point); | |
| isFirstPoint = true; | |
| } | |
| break; | |
| default: | |
| console.warn(command); | |
| } | |
| doSetFirstPoint = false; | |
| } | |
| return path; | |
| } | |
| function parseCSSStylesheet(node) { | |
| if (!node.sheet || !node.sheet.cssRules || !node.sheet.cssRules.length) | |
| return; | |
| for (let i = 0; i < node.sheet.cssRules.length; i++) { | |
| const stylesheet = node.sheet.cssRules[i]; | |
| if (stylesheet.type !== 1) | |
| continue; | |
| const selectorList = stylesheet.selectorText.split(/,/gm).filter(Boolean).map((i2) => i2.trim()); | |
| for (let j = 0; j < selectorList.length; j++) { | |
| const definitions = Object.fromEntries(Object.entries(stylesheet.style).filter(([, v]) => v !== "")); | |
| stylesheets[selectorList[j]] = Object.assign(stylesheets[selectorList[j]] || {}, definitions); | |
| } | |
| } | |
| } | |
| function parseArcCommand(path, rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, start, end) { | |
| if (rx == 0 || ry == 0) { | |
| path.lineTo(end.x, end.y); | |
| return; | |
| } | |
| x_axis_rotation = x_axis_rotation * Math.PI / 180; | |
| rx = Math.abs(rx); | |
| ry = Math.abs(ry); | |
| const dx2 = (start.x - end.x) / 2; | |
| const dy2 = (start.y - end.y) / 2; | |
| const x1p = Math.cos(x_axis_rotation) * dx2 + Math.sin(x_axis_rotation) * dy2; | |
| const y1p = -Math.sin(x_axis_rotation) * dx2 + Math.cos(x_axis_rotation) * dy2; | |
| let rxs = rx * rx; | |
| let rys = ry * ry; | |
| const x1ps = x1p * x1p; | |
| const y1ps = y1p * y1p; | |
| const cr = x1ps / rxs + y1ps / rys; | |
| if (cr > 1) { | |
| const s = Math.sqrt(cr); | |
| rx = s * rx; | |
| ry = s * ry; | |
| rxs = rx * rx; | |
| rys = ry * ry; | |
| } | |
| const dq = rxs * y1ps + rys * x1ps; | |
| const pq = (rxs * rys - dq) / dq; | |
| let q = Math.sqrt(Math.max(0, pq)); | |
| if (large_arc_flag === sweep_flag) | |
| q = -q; | |
| const cxp = q * rx * y1p / ry; | |
| const cyp = -q * ry * x1p / rx; | |
| const cx = Math.cos(x_axis_rotation) * cxp - Math.sin(x_axis_rotation) * cyp + (start.x + end.x) / 2; | |
| const cy = Math.sin(x_axis_rotation) * cxp + Math.cos(x_axis_rotation) * cyp + (start.y + end.y) / 2; | |
| const theta = svgAngle(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry); | |
| const delta = svgAngle((x1p - cxp) / rx, (y1p - cyp) / ry, (-x1p - cxp) / rx, (-y1p - cyp) / ry) % (Math.PI * 2); | |
| path.currentPath.absellipse(cx, cy, rx, ry, theta, theta + delta, sweep_flag === 0, x_axis_rotation); | |
| } | |
| function svgAngle(ux, uy, vx, vy) { | |
| const dot = ux * vx + uy * vy; | |
| const len = Math.sqrt(ux * ux + uy * uy) * Math.sqrt(vx * vx + vy * vy); | |
| let ang = Math.acos(Math.max(-1, Math.min(1, dot / len))); | |
| if (ux * vy - uy * vx < 0) | |
| ang = -ang; | |
| return ang; | |
| } | |
| function parseRectNode(node) { | |
| const x = parseFloatWithUnits(node.getAttribute("x") || 0); | |
| const y = parseFloatWithUnits(node.getAttribute("y") || 0); | |
| const rx = parseFloatWithUnits(node.getAttribute("rx") || node.getAttribute("ry") || 0); | |
| const ry = parseFloatWithUnits(node.getAttribute("ry") || node.getAttribute("rx") || 0); | |
| const w = parseFloatWithUnits(node.getAttribute("width")); | |
| const h = parseFloatWithUnits(node.getAttribute("height")); | |
| const bci = 1 - 0.551915024494; | |
| const path = new THREE.ShapePath(); | |
| path.moveTo(x + rx, y); | |
| path.lineTo(x + w - rx, y); | |
| if (rx !== 0 || ry !== 0) { | |
| path.bezierCurveTo(x + w - rx * bci, y, x + w, y + ry * bci, x + w, y + ry); | |
| } | |
| path.lineTo(x + w, y + h - ry); | |
| if (rx !== 0 || ry !== 0) { | |
| path.bezierCurveTo(x + w, y + h - ry * bci, x + w - rx * bci, y + h, x + w - rx, y + h); | |
| } | |
| path.lineTo(x + rx, y + h); | |
| if (rx !== 0 || ry !== 0) { | |
| path.bezierCurveTo(x + rx * bci, y + h, x, y + h - ry * bci, x, y + h - ry); | |
| } | |
| path.lineTo(x, y + ry); | |
| if (rx !== 0 || ry !== 0) { | |
| path.bezierCurveTo(x, y + ry * bci, x + rx * bci, y, x + rx, y); | |
| } | |
| return path; | |
| } | |
| function parsePolygonNode(node) { | |
| function iterator(match, a, b) { | |
| const x = parseFloatWithUnits(a); | |
| const y = parseFloatWithUnits(b); | |
| if (index === 0) { | |
| path.moveTo(x, y); | |
| } else { | |
| path.lineTo(x, y); | |
| } | |
| index++; | |
| } | |
| const regex = /([+-]?\d*\.?\d+(?:e[+-]?\d+)?)(?:,|\s)([+-]?\d*\.?\d+(?:e[+-]?\d+)?)/g; | |
| const path = new THREE.ShapePath(); | |
| let index = 0; | |
| node.getAttribute("points").replace(regex, iterator); | |
| path.currentPath.autoClose = true; | |
| return path; | |
| } | |
| function parsePolylineNode(node) { | |
| function iterator(match, a, b) { | |
| const x = parseFloatWithUnits(a); | |
| const y = parseFloatWithUnits(b); | |
| if (index === 0) { | |
| path.moveTo(x, y); | |
| } else { | |
| path.lineTo(x, y); | |
| } | |
| index++; | |
| } | |
| const regex = /([+-]?\d*\.?\d+(?:e[+-]?\d+)?)(?:,|\s)([+-]?\d*\.?\d+(?:e[+-]?\d+)?)/g; | |
| const path = new THREE.ShapePath(); | |
| let index = 0; | |
| node.getAttribute("points").replace(regex, iterator); | |
| path.currentPath.autoClose = false; | |
| return path; | |
| } | |
| function parseCircleNode(node) { | |
| const x = parseFloatWithUnits(node.getAttribute("cx") || 0); | |
| const y = parseFloatWithUnits(node.getAttribute("cy") || 0); | |
| const r = parseFloatWithUnits(node.getAttribute("r") || 0); | |
| const subpath = new THREE.Path(); | |
| subpath.absarc(x, y, r, 0, Math.PI * 2); | |
| const path = new THREE.ShapePath(); | |
| path.subPaths.push(subpath); | |
| return path; | |
| } | |
| function parseEllipseNode(node) { | |
| const x = parseFloatWithUnits(node.getAttribute("cx") || 0); | |
| const y = parseFloatWithUnits(node.getAttribute("cy") || 0); | |
| const rx = parseFloatWithUnits(node.getAttribute("rx") || 0); | |
| const ry = parseFloatWithUnits(node.getAttribute("ry") || 0); | |
| const subpath = new THREE.Path(); | |
| subpath.absellipse(x, y, rx, ry, 0, Math.PI * 2); | |
| const path = new THREE.ShapePath(); | |
| path.subPaths.push(subpath); | |
| return path; | |
| } | |
| function parseLineNode(node) { | |
| const x1 = parseFloatWithUnits(node.getAttribute("x1") || 0); | |
| const y1 = parseFloatWithUnits(node.getAttribute("y1") || 0); | |
| const x2 = parseFloatWithUnits(node.getAttribute("x2") || 0); | |
| const y2 = parseFloatWithUnits(node.getAttribute("y2") || 0); | |
| const path = new THREE.ShapePath(); | |
| path.moveTo(x1, y1); | |
| path.lineTo(x2, y2); | |
| path.currentPath.autoClose = false; | |
| return path; | |
| } | |
| function parseStyle(node, style) { | |
| style = Object.assign({}, style); | |
| let stylesheetStyles = {}; | |
| if (node.hasAttribute("class")) { | |
| const classSelectors = node.getAttribute("class").split(/\s/).filter(Boolean).map((i) => i.trim()); | |
| for (let i = 0; i < classSelectors.length; i++) { | |
| stylesheetStyles = Object.assign(stylesheetStyles, stylesheets["." + classSelectors[i]]); | |
| } | |
| } | |
| if (node.hasAttribute("id")) { | |
| stylesheetStyles = Object.assign(stylesheetStyles, stylesheets["#" + node.getAttribute("id")]); | |
| } | |
| function addStyle(svgName, jsName, adjustFunction) { | |
| if (adjustFunction === void 0) | |
| adjustFunction = function copy(v) { | |
| if (v.startsWith("url")) | |
| console.warn("SVGLoader: url access in attributes is not implemented."); | |
| return v; | |
| }; | |
| if (node.hasAttribute(svgName)) | |
| style[jsName] = adjustFunction(node.getAttribute(svgName)); | |
| if (stylesheetStyles[svgName]) | |
| style[jsName] = adjustFunction(stylesheetStyles[svgName]); | |
| if (node.style && node.style[svgName] !== "") | |
| style[jsName] = adjustFunction(node.style[svgName]); | |
| } | |
| function clamp(v) { | |
| return Math.max(0, Math.min(1, parseFloatWithUnits(v))); | |
| } | |
| function positive(v) { | |
| return Math.max(0, parseFloatWithUnits(v)); | |
| } | |
| addStyle("fill", "fill"); | |
| addStyle("fill-opacity", "fillOpacity", clamp); | |
| addStyle("fill-rule", "fillRule"); | |
| addStyle("opacity", "opacity", clamp); | |
| addStyle("stroke", "stroke"); | |
| addStyle("stroke-opacity", "strokeOpacity", clamp); | |
| addStyle("stroke-width", "strokeWidth", positive); | |
| addStyle("stroke-linejoin", "strokeLineJoin"); | |
| addStyle("stroke-linecap", "strokeLineCap"); | |
| addStyle("stroke-miterlimit", "strokeMiterLimit", positive); | |
| addStyle("visibility", "visibility"); | |
| return style; | |
| } | |
| function getReflection(a, b) { | |
| return a - (b - a); | |
| } | |
| function parseFloats(input, flags, stride) { | |
| if (typeof input !== "string") { | |
| throw new TypeError("Invalid input: " + typeof input); | |
| } | |
| const RE = { | |
| SEPARATOR: /[ \t\r\n\,.\-+]/, | |
| WHITESPACE: /[ \t\r\n]/, | |
| DIGIT: /[\d]/, | |
| SIGN: /[-+]/, | |
| POINT: /\./, | |
| COMMA: /,/, | |
| EXP: /e/i, | |
| FLAGS: /[01]/ | |
| }; | |
| const SEP = 0; | |
| const INT = 1; | |
| const FLOAT = 2; | |
| const EXP = 3; | |
| let state = SEP; | |
| let seenComma = true; | |
| let number = "", exponent = ""; | |
| const result = []; | |
| function throwSyntaxError(current2, i, partial) { | |
| const error = new SyntaxError('Unexpected character "' + current2 + '" at index ' + i + "."); | |
| error.partial = partial; | |
| throw error; | |
| } | |
| function newNumber() { | |
| if (number !== "") { | |
| if (exponent === "") | |
| result.push(Number(number)); | |
| else | |
| result.push(Number(number) * Math.pow(10, Number(exponent))); | |
| } | |
| number = ""; | |
| exponent = ""; | |
| } | |
| let current; | |
| const length = input.length; | |
| for (let i = 0; i < length; i++) { | |
| current = input[i]; | |
| if (Array.isArray(flags) && flags.includes(result.length % stride) && RE.FLAGS.test(current)) { | |
| state = INT; | |
| number = current; | |
| newNumber(); | |
| continue; | |
| } | |
| if (state === SEP) { | |
| if (RE.WHITESPACE.test(current)) { | |
| continue; | |
| } | |
| if (RE.DIGIT.test(current) || RE.SIGN.test(current)) { | |
| state = INT; | |
| number = current; | |
| continue; | |
| } | |
| if (RE.POINT.test(current)) { | |
| state = FLOAT; | |
| number = current; | |
| continue; | |
| } | |
| if (RE.COMMA.test(current)) { | |
| if (seenComma) { | |
| throwSyntaxError(current, i, result); | |
| } | |
| seenComma = true; | |
| } | |
| } | |
| if (state === INT) { | |
| if (RE.DIGIT.test(current)) { | |
| number += current; | |
| continue; | |
| } | |
| if (RE.POINT.test(current)) { | |
| number += current; | |
| state = FLOAT; | |
| continue; | |
| } | |
| if (RE.EXP.test(current)) { | |
| state = EXP; | |
| continue; | |
| } | |
| if (RE.SIGN.test(current) && number.length === 1 && RE.SIGN.test(number[0])) { | |
| throwSyntaxError(current, i, result); | |
| } | |
| } | |
| if (state === FLOAT) { | |
| if (RE.DIGIT.test(current)) { | |
| number += current; | |
| continue; | |
| } | |
| if (RE.EXP.test(current)) { | |
| state = EXP; | |
| continue; | |
| } | |
| if (RE.POINT.test(current) && number[number.length - 1] === ".") { | |
| throwSyntaxError(current, i, result); | |
| } | |
| } | |
| if (state === EXP) { | |
| if (RE.DIGIT.test(current)) { | |
| exponent += current; | |
| continue; | |
| } | |
| if (RE.SIGN.test(current)) { | |
| if (exponent === "") { | |
| exponent += current; | |
| continue; | |
| } | |
| if (exponent.length === 1 && RE.SIGN.test(exponent)) { | |
| throwSyntaxError(current, i, result); | |
| } | |
| } | |
| } | |
| if (RE.WHITESPACE.test(current)) { | |
| newNumber(); | |
| state = SEP; | |
| seenComma = false; | |
| } else if (RE.COMMA.test(current)) { | |
| newNumber(); | |
| state = SEP; | |
| seenComma = true; | |
| } else if (RE.SIGN.test(current)) { | |
| newNumber(); | |
| state = INT; | |
| number = current; | |
| } else if (RE.POINT.test(current)) { | |
| newNumber(); | |
| state = FLOAT; | |
| number = current; | |
| } else { | |
| throwSyntaxError(current, i, result); | |
| } | |
| } | |
| newNumber(); | |
| return result; | |
| } | |
| const units = ["mm", "cm", "in", "pt", "pc", "px"]; | |
| const unitConversion = { | |
| mm: { | |
| mm: 1, | |
| cm: 0.1, | |
| in: 1 / 25.4, | |
| pt: 72 / 25.4, | |
| pc: 6 / 25.4, | |
| px: -1 | |
| }, | |
| cm: { | |
| mm: 10, | |
| cm: 1, | |
| in: 1 / 2.54, | |
| pt: 72 / 2.54, | |
| pc: 6 / 2.54, | |
| px: -1 | |
| }, | |
| in: { | |
| mm: 25.4, | |
| cm: 2.54, | |
| in: 1, | |
| pt: 72, | |
| pc: 6, | |
| px: -1 | |
| }, | |
| pt: { | |
| mm: 25.4 / 72, | |
| cm: 2.54 / 72, | |
| in: 1 / 72, | |
| pt: 1, | |
| pc: 6 / 72, | |
| px: -1 | |
| }, | |
| pc: { | |
| mm: 25.4 / 6, | |
| cm: 2.54 / 6, | |
| in: 1 / 6, | |
| pt: 72 / 6, | |
| pc: 1, | |
| px: -1 | |
| }, | |
| px: { | |
| px: 1 | |
| } | |
| }; | |
| function parseFloatWithUnits(string) { | |
| let theUnit = "px"; | |
| if (typeof string === "string" || string instanceof String) { | |
| for (let i = 0, n = units.length; i < n; i++) { | |
| const u = units[i]; | |
| if (string.endsWith(u)) { | |
| theUnit = u; | |
| string = string.substring(0, string.length - u.length); | |
| break; | |
| } | |
| } | |
| } | |
| let scale = void 0; | |
| if (theUnit === "px" && scope.defaultUnit !== "px") { | |
| scale = unitConversion["in"][scope.defaultUnit] / scope.defaultDPI; | |
| } else { | |
| scale = unitConversion[theUnit][scope.defaultUnit]; | |
| if (scale < 0) { | |
| scale = unitConversion[theUnit]["in"] * scope.defaultDPI; | |
| } | |
| } | |
| return scale * parseFloat(string); | |
| } | |
| function getNodeTransform(node) { | |
| if (!(node.hasAttribute("transform") || node.nodeName === "use" && (node.hasAttribute("x") || node.hasAttribute("y")))) { | |
| return null; | |
| } | |
| const transform = parseNodeTransform(node); | |
| if (transformStack.length > 0) { | |
| transform.premultiply(transformStack[transformStack.length - 1]); | |
| } | |
| currentTransform.copy(transform); | |
| transformStack.push(transform); | |
| return transform; | |
| } | |
| function parseNodeTransform(node) { | |
| const transform = new THREE.Matrix3(); | |
| const currentTransform2 = tempTransform0; | |
| if (node.nodeName === "use" && (node.hasAttribute("x") || node.hasAttribute("y"))) { | |
| const tx = parseFloatWithUnits(node.getAttribute("x")); | |
| const ty = parseFloatWithUnits(node.getAttribute("y")); | |
| transform.translate(tx, ty); | |
| } | |
| if (node.hasAttribute("transform")) { | |
| const transformsTexts = node.getAttribute("transform").split(")"); | |
| for (let tIndex = transformsTexts.length - 1; tIndex >= 0; tIndex--) { | |
| const transformText = transformsTexts[tIndex].trim(); | |
| if (transformText === "") | |
| continue; | |
| const openParPos = transformText.indexOf("("); | |
| const closeParPos = transformText.length; | |
| if (openParPos > 0 && openParPos < closeParPos) { | |
| const transformType = transformText.slice(0, openParPos); | |
| const array = parseFloats(transformText.slice(openParPos + 1)); | |
| currentTransform2.identity(); | |
| switch (transformType) { | |
| case "translate": | |
| if (array.length >= 1) { | |
| const tx = array[0]; | |
| let ty = 0; | |
| if (array.length >= 2) { | |
| ty = array[1]; | |
| } | |
| currentTransform2.translate(tx, ty); | |
| } | |
| break; | |
| case "rotate": | |
| if (array.length >= 1) { | |
| let angle = 0; | |
| let cx = 0; | |
| let cy = 0; | |
| angle = array[0] * Math.PI / 180; | |
| if (array.length >= 3) { | |
| cx = array[1]; | |
| cy = array[2]; | |
| } | |
| tempTransform1.makeTranslation(-cx, -cy); | |
| tempTransform2.makeRotation(angle); | |
| tempTransform3.multiplyMatrices(tempTransform2, tempTransform1); | |
| tempTransform1.makeTranslation(cx, cy); | |
| currentTransform2.multiplyMatrices(tempTransform1, tempTransform3); | |
| } | |
| break; | |
| case "scale": | |
| if (array.length >= 1) { | |
| const scaleX = array[0]; | |
| let scaleY = scaleX; | |
| if (array.length >= 2) { | |
| scaleY = array[1]; | |
| } | |
| currentTransform2.scale(scaleX, scaleY); | |
| } | |
| break; | |
| case "skewX": | |
| if (array.length === 1) { | |
| currentTransform2.set(1, Math.tan(array[0] * Math.PI / 180), 0, 0, 1, 0, 0, 0, 1); | |
| } | |
| break; | |
| case "skewY": | |
| if (array.length === 1) { | |
| currentTransform2.set(1, 0, 0, Math.tan(array[0] * Math.PI / 180), 1, 0, 0, 0, 1); | |
| } | |
| break; | |
| case "matrix": | |
| if (array.length === 6) { | |
| currentTransform2.set(array[0], array[2], array[4], array[1], array[3], array[5], 0, 0, 1); | |
| } | |
| break; | |
| } | |
| } | |
| transform.premultiply(currentTransform2); | |
| } | |
| } | |
| return transform; | |
| } | |
| function transformPath(path, m) { | |
| function transfVec2(v2) { | |
| tempV3.set(v2.x, v2.y, 1).applyMatrix3(m); | |
| v2.set(tempV3.x, tempV3.y); | |
| } | |
| function transfEllipseGeneric(curve) { | |
| const a = curve.xRadius; | |
| const b = curve.yRadius; | |
| const cosTheta = Math.cos(curve.aRotation); | |
| const sinTheta = Math.sin(curve.aRotation); | |
| const v1 = new THREE.Vector3(a * cosTheta, a * sinTheta, 0); | |
| const v2 = new THREE.Vector3(-b * sinTheta, b * cosTheta, 0); | |
| const f1 = v1.applyMatrix3(m); | |
| const f2 = v2.applyMatrix3(m); | |
| const mF = tempTransform0.set(f1.x, f2.x, 0, f1.y, f2.y, 0, 0, 0, 1); | |
| const mFInv = tempTransform1.copy(mF).invert(); | |
| const mFInvT = tempTransform2.copy(mFInv).transpose(); | |
| const mQ = mFInvT.multiply(mFInv); | |
| const mQe = mQ.elements; | |
| const ed = eigenDecomposition(mQe[0], mQe[1], mQe[4]); | |
| const rt1sqrt = Math.sqrt(ed.rt1); | |
| const rt2sqrt = Math.sqrt(ed.rt2); | |
| curve.xRadius = 1 / rt1sqrt; | |
| curve.yRadius = 1 / rt2sqrt; | |
| curve.aRotation = Math.atan2(ed.sn, ed.cs); | |
| const isFullEllipse = (curve.aEndAngle - curve.aStartAngle) % (2 * Math.PI) < Number.EPSILON; | |
| if (!isFullEllipse) { | |
| const mDsqrt = tempTransform1.set(rt1sqrt, 0, 0, 0, rt2sqrt, 0, 0, 0, 1); | |
| const mRT = tempTransform2.set(ed.cs, ed.sn, 0, -ed.sn, ed.cs, 0, 0, 0, 1); | |
| const mDRF = mDsqrt.multiply(mRT).multiply(mF); | |
| const transformAngle = (phi) => { | |
| const { x: cosR, y: sinR } = new THREE.Vector3(Math.cos(phi), Math.sin(phi), 0).applyMatrix3(mDRF); | |
| return Math.atan2(sinR, cosR); | |
| }; | |
| curve.aStartAngle = transformAngle(curve.aStartAngle); | |
| curve.aEndAngle = transformAngle(curve.aEndAngle); | |
| if (isTransformFlipped(m)) { | |
| curve.aClockwise = !curve.aClockwise; | |
| } | |
| } | |
| } | |
| function transfEllipseNoSkew(curve) { | |
| const sx = getTransformScaleX(m); | |
| const sy = getTransformScaleY(m); | |
| curve.xRadius *= sx; | |
| curve.yRadius *= sy; | |
| const theta = sx > Number.EPSILON ? Math.atan2(m.elements[1], m.elements[0]) : Math.atan2(-m.elements[3], m.elements[4]); | |
| curve.aRotation += theta; | |
| if (isTransformFlipped(m)) { | |
| curve.aStartAngle *= -1; | |
| curve.aEndAngle *= -1; | |
| curve.aClockwise = !curve.aClockwise; | |
| } | |
| } | |
| const subPaths = path.subPaths; | |
| for (let i = 0, n = subPaths.length; i < n; i++) { | |
| const subPath = subPaths[i]; | |
| const curves = subPath.curves; | |
| for (let j = 0; j < curves.length; j++) { | |
| const curve = curves[j]; | |
| if (curve.isLineCurve) { | |
| transfVec2(curve.v1); | |
| transfVec2(curve.v2); | |
| } else if (curve.isCubicBezierCurve) { | |
| transfVec2(curve.v0); | |
| transfVec2(curve.v1); | |
| transfVec2(curve.v2); | |
| transfVec2(curve.v3); | |
| } else if (curve.isQuadraticBezierCurve) { | |
| transfVec2(curve.v0); | |
| transfVec2(curve.v1); | |
| transfVec2(curve.v2); | |
| } else if (curve.isEllipseCurve) { | |
| tempV2.set(curve.aX, curve.aY); | |
| transfVec2(tempV2); | |
| curve.aX = tempV2.x; | |
| curve.aY = tempV2.y; | |
| if (isTransformSkewed(m)) { | |
| transfEllipseGeneric(curve); | |
| } else { | |
| transfEllipseNoSkew(curve); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| function isTransformFlipped(m) { | |
| const te = m.elements; | |
| return te[0] * te[4] - te[1] * te[3] < 0; | |
| } | |
| function isTransformSkewed(m) { | |
| const te = m.elements; | |
| const basisDot = te[0] * te[3] + te[1] * te[4]; | |
| if (basisDot === 0) | |
| return false; | |
| const sx = getTransformScaleX(m); | |
| const sy = getTransformScaleY(m); | |
| return Math.abs(basisDot / (sx * sy)) > Number.EPSILON; | |
| } | |
| function getTransformScaleX(m) { | |
| const te = m.elements; | |
| return Math.sqrt(te[0] * te[0] + te[1] * te[1]); | |
| } | |
| function getTransformScaleY(m) { | |
| const te = m.elements; | |
| return Math.sqrt(te[3] * te[3] + te[4] * te[4]); | |
| } | |
| function eigenDecomposition(A, B, C) { | |
| let rt1, rt2, cs, sn, t; | |
| const sm = A + C; | |
| const df = A - C; | |
| const rt = Math.sqrt(df * df + 4 * B * B); | |
| if (sm > 0) { | |
| rt1 = 0.5 * (sm + rt); | |
| t = 1 / rt1; | |
| rt2 = A * t * C - B * t * B; | |
| } else if (sm < 0) { | |
| rt2 = 0.5 * (sm - rt); | |
| } else { | |
| rt1 = 0.5 * rt; | |
| rt2 = -0.5 * rt; | |
| } | |
| if (df > 0) { | |
| cs = df + rt; | |
| } else { | |
| cs = df - rt; | |
| } | |
| if (Math.abs(cs) > 2 * Math.abs(B)) { | |
| t = -2 * B / cs; | |
| sn = 1 / Math.sqrt(1 + t * t); | |
| cs = t * sn; | |
| } else if (Math.abs(B) === 0) { | |
| cs = 1; | |
| sn = 0; | |
| } else { | |
| t = -0.5 * cs / B; | |
| cs = 1 / Math.sqrt(1 + t * t); | |
| sn = t * cs; | |
| } | |
| if (df > 0) { | |
| t = cs; | |
| cs = -sn; | |
| sn = t; | |
| } | |
| return { rt1, rt2, cs, sn }; | |
| } | |
| const paths = []; | |
| const stylesheets = {}; | |
| const transformStack = []; | |
| const tempTransform0 = new THREE.Matrix3(); | |
| const tempTransform1 = new THREE.Matrix3(); | |
| const tempTransform2 = new THREE.Matrix3(); | |
| const tempTransform3 = new THREE.Matrix3(); | |
| const tempV2 = new THREE.Vector2(); | |
| const tempV3 = new THREE.Vector3(); | |
| const currentTransform = new THREE.Matrix3(); | |
| const xml = new DOMParser().parseFromString(text, "image/svg+xml"); | |
| parseNode(xml.documentElement, { | |
| fill: "#000", | |
| fillOpacity: 1, | |
| strokeOpacity: 1, | |
| strokeWidth: 1, | |
| strokeLineJoin: "miter", | |
| strokeLineCap: "butt", | |
| strokeMiterLimit: 4 | |
| }); | |
| const data = { paths, xml: xml.documentElement }; | |
| return data; | |
| } | |
| static createShapes(shapePath) { | |
| const BIGNUMBER = 999999999; | |
| const IntersectionLocationType = { | |
| ORIGIN: 0, | |
| DESTINATION: 1, | |
| BETWEEN: 2, | |
| LEFT: 3, | |
| RIGHT: 4, | |
| BEHIND: 5, | |
| BEYOND: 6 | |
| }; | |
| const classifyResult = { | |
| loc: IntersectionLocationType.ORIGIN, | |
| t: 0 | |
| }; | |
| function findEdgeIntersection(a0, a1, b0, b1) { | |
| const x1 = a0.x; | |
| const x2 = a1.x; | |
| const x3 = b0.x; | |
| const x4 = b1.x; | |
| const y1 = a0.y; | |
| const y2 = a1.y; | |
| const y3 = b0.y; | |
| const y4 = b1.y; | |
| const nom1 = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3); | |
| const nom2 = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3); | |
| const denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); | |
| const t1 = nom1 / denom; | |
| const t2 = nom2 / denom; | |
| if (denom === 0 && nom1 !== 0 || t1 <= 0 || t1 >= 1 || t2 < 0 || t2 > 1) { | |
| return null; | |
| } else if (nom1 === 0 && denom === 0) { | |
| for (let i = 0; i < 2; i++) { | |
| classifyPoint(i === 0 ? b0 : b1, a0, a1); | |
| if (classifyResult.loc == IntersectionLocationType.ORIGIN) { | |
| const point = i === 0 ? b0 : b1; | |
| return { x: point.x, y: point.y, t: classifyResult.t }; | |
| } else if (classifyResult.loc == IntersectionLocationType.BETWEEN) { | |
| const x = +(x1 + classifyResult.t * (x2 - x1)).toPrecision(10); | |
| const y = +(y1 + classifyResult.t * (y2 - y1)).toPrecision(10); | |
| return { x, y, t: classifyResult.t }; | |
| } | |
| } | |
| return null; | |
| } else { | |
| for (let i = 0; i < 2; i++) { | |
| classifyPoint(i === 0 ? b0 : b1, a0, a1); | |
| if (classifyResult.loc == IntersectionLocationType.ORIGIN) { | |
| const point = i === 0 ? b0 : b1; | |
| return { x: point.x, y: point.y, t: classifyResult.t }; | |
| } | |
| } | |
| const x = +(x1 + t1 * (x2 - x1)).toPrecision(10); | |
| const y = +(y1 + t1 * (y2 - y1)).toPrecision(10); | |
| return { x, y, t: t1 }; | |
| } | |
| } | |
| function classifyPoint(p, edgeStart, edgeEnd) { | |
| const ax = edgeEnd.x - edgeStart.x; | |
| const ay = edgeEnd.y - edgeStart.y; | |
| const bx = p.x - edgeStart.x; | |
| const by = p.y - edgeStart.y; | |
| const sa = ax * by - bx * ay; | |
| if (p.x === edgeStart.x && p.y === edgeStart.y) { | |
| classifyResult.loc = IntersectionLocationType.ORIGIN; | |
| classifyResult.t = 0; | |
| return; | |
| } | |
| if (p.x === edgeEnd.x && p.y === edgeEnd.y) { | |
| classifyResult.loc = IntersectionLocationType.DESTINATION; | |
| classifyResult.t = 1; | |
| return; | |
| } | |
| if (sa < -Number.EPSILON) { | |
| classifyResult.loc = IntersectionLocationType.LEFT; | |
| return; | |
| } | |
| if (sa > Number.EPSILON) { | |
| classifyResult.loc = IntersectionLocationType.RIGHT; | |
| return; | |
| } | |
| if (ax * bx < 0 || ay * by < 0) { | |
| classifyResult.loc = IntersectionLocationType.BEHIND; | |
| return; | |
| } | |
| if (Math.sqrt(ax * ax + ay * ay) < Math.sqrt(bx * bx + by * by)) { | |
| classifyResult.loc = IntersectionLocationType.BEYOND; | |
| return; | |
| } | |
| let t; | |
| if (ax !== 0) { | |
| t = bx / ax; | |
| } else { | |
| t = by / ay; | |
| } | |
| classifyResult.loc = IntersectionLocationType.BETWEEN; | |
| classifyResult.t = t; | |
| } | |
| function getIntersections(path1, path2) { | |
| const intersectionsRaw = []; | |
| const intersections = []; | |
| for (let index = 1; index < path1.length; index++) { | |
| const path1EdgeStart = path1[index - 1]; | |
| const path1EdgeEnd = path1[index]; | |
| for (let index2 = 1; index2 < path2.length; index2++) { | |
| const path2EdgeStart = path2[index2 - 1]; | |
| const path2EdgeEnd = path2[index2]; | |
| const intersection = findEdgeIntersection(path1EdgeStart, path1EdgeEnd, path2EdgeStart, path2EdgeEnd); | |
| if (intersection !== null && intersectionsRaw.find( | |
| (i) => i.t <= intersection.t + Number.EPSILON && i.t >= intersection.t - Number.EPSILON | |
| ) === void 0) { | |
| intersectionsRaw.push(intersection); | |
| intersections.push(new THREE.Vector2(intersection.x, intersection.y)); | |
| } | |
| } | |
| } | |
| return intersections; | |
| } | |
| function getScanlineIntersections(scanline, boundingBox, paths) { | |
| const center = new THREE.Vector2(); | |
| boundingBox.getCenter(center); | |
| const allIntersections = []; | |
| paths.forEach((path) => { | |
| if (path.boundingBox.containsPoint(center)) { | |
| const intersections = getIntersections(scanline, path.points); | |
| intersections.forEach((p) => { | |
| allIntersections.push({ identifier: path.identifier, isCW: path.isCW, point: p }); | |
| }); | |
| } | |
| }); | |
| allIntersections.sort((i1, i2) => { | |
| return i1.point.x - i2.point.x; | |
| }); | |
| return allIntersections; | |
| } | |
| function isHoleTo(simplePath, allPaths, scanlineMinX2, scanlineMaxX2, _fillRule) { | |
| if (_fillRule === null || _fillRule === void 0 || _fillRule === "") { | |
| _fillRule = "nonzero"; | |
| } | |
| const centerBoundingBox = new THREE.Vector2(); | |
| simplePath.boundingBox.getCenter(centerBoundingBox); | |
| const scanline = [ | |
| new THREE.Vector2(scanlineMinX2, centerBoundingBox.y), | |
| new THREE.Vector2(scanlineMaxX2, centerBoundingBox.y) | |
| ]; | |
| const scanlineIntersections = getScanlineIntersections(scanline, simplePath.boundingBox, allPaths); | |
| scanlineIntersections.sort((i1, i2) => { | |
| return i1.point.x - i2.point.x; | |
| }); | |
| const baseIntersections = []; | |
| const otherIntersections = []; | |
| scanlineIntersections.forEach((i2) => { | |
| if (i2.identifier === simplePath.identifier) { | |
| baseIntersections.push(i2); | |
| } else { | |
| otherIntersections.push(i2); | |
| } | |
| }); | |
| const firstXOfPath = baseIntersections[0].point.x; | |
| const stack = []; | |
| let i = 0; | |
| while (i < otherIntersections.length && otherIntersections[i].point.x < firstXOfPath) { | |
| if (stack.length > 0 && stack[stack.length - 1] === otherIntersections[i].identifier) { | |
| stack.pop(); | |
| } else { | |
| stack.push(otherIntersections[i].identifier); | |
| } | |
| i++; | |
| } | |
| stack.push(simplePath.identifier); | |
| if (_fillRule === "evenodd") { | |
| const isHole = stack.length % 2 === 0 ? true : false; | |
| const isHoleFor = stack[stack.length - 2]; | |
| return { identifier: simplePath.identifier, isHole, for: isHoleFor }; | |
| } else if (_fillRule === "nonzero") { | |
| let isHole = true; | |
| let isHoleFor = null; | |
| let lastCWValue = null; | |
| for (let i2 = 0; i2 < stack.length; i2++) { | |
| const identifier = stack[i2]; | |
| if (isHole) { | |
| lastCWValue = allPaths[identifier].isCW; | |
| isHole = false; | |
| isHoleFor = identifier; | |
| } else if (lastCWValue !== allPaths[identifier].isCW) { | |
| lastCWValue = allPaths[identifier].isCW; | |
| isHole = true; | |
| } | |
| } | |
| return { identifier: simplePath.identifier, isHole, for: isHoleFor }; | |
| } else { | |
| console.warn('fill-rule: "' + _fillRule + '" is currently not implemented.'); | |
| } | |
| } | |
| let scanlineMinX = BIGNUMBER; | |
| let scanlineMaxX = -BIGNUMBER; | |
| let simplePaths = shapePath.subPaths.map((p) => { | |
| const points = p.getPoints(); | |
| let maxY = -BIGNUMBER; | |
| let minY = BIGNUMBER; | |
| let maxX = -BIGNUMBER; | |
| let minX = BIGNUMBER; | |
| for (let i = 0; i < points.length; i++) { | |
| const p2 = points[i]; | |
| if (p2.y > maxY) { | |
| maxY = p2.y; | |
| } | |
| if (p2.y < minY) { | |
| minY = p2.y; | |
| } | |
| if (p2.x > maxX) { | |
| maxX = p2.x; | |
| } | |
| if (p2.x < minX) { | |
| minX = p2.x; | |
| } | |
| } | |
| if (scanlineMaxX <= maxX) { | |
| scanlineMaxX = maxX + 1; | |
| } | |
| if (scanlineMinX >= minX) { | |
| scanlineMinX = minX - 1; | |
| } | |
| return { | |
| curves: p.curves, | |
| points, | |
| isCW: THREE.ShapeUtils.isClockWise(points), | |
| identifier: -1, | |
| boundingBox: new THREE.Box2(new THREE.Vector2(minX, minY), new THREE.Vector2(maxX, maxY)) | |
| }; | |
| }); | |
| simplePaths = simplePaths.filter((sp) => sp.points.length > 1); | |
| for (let identifier = 0; identifier < simplePaths.length; identifier++) { | |
| simplePaths[identifier].identifier = identifier; | |
| } | |
| const isAHole = simplePaths.map( | |
| (p) => isHoleTo( | |
| p, | |
| simplePaths, | |
| scanlineMinX, | |
| scanlineMaxX, | |
| shapePath.userData ? shapePath.userData.style.fillRule : void 0 | |
| ) | |
| ); | |
| const shapesToReturn = []; | |
| simplePaths.forEach((p) => { | |
| const amIAHole = isAHole[p.identifier]; | |
| if (!amIAHole.isHole) { | |
| const shape = new THREE.Shape(); | |
| shape.curves = p.curves; | |
| const holes = isAHole.filter((h) => h.isHole && h.for === p.identifier); | |
| holes.forEach((h) => { | |
| const hole = simplePaths[h.identifier]; | |
| const path = new THREE.Path(); | |
| path.curves = hole.curves; | |
| shape.holes.push(path); | |
| }); | |
| shapesToReturn.push(shape); | |
| } | |
| }); | |
| return shapesToReturn; | |
| } | |
| static getStrokeStyle(width, color, lineJoin, lineCap, miterLimit) { | |
| width = width !== void 0 ? width : 1; | |
| color = color !== void 0 ? color : "#000"; | |
| lineJoin = lineJoin !== void 0 ? lineJoin : "miter"; | |
| lineCap = lineCap !== void 0 ? lineCap : "butt"; | |
| miterLimit = miterLimit !== void 0 ? miterLimit : 4; | |
| return { | |
| strokeColor: color, | |
| strokeWidth: width, | |
| strokeLineJoin: lineJoin, | |
| strokeLineCap: lineCap, | |
| strokeMiterLimit: miterLimit | |
| }; | |
| } | |
| static pointsToStroke(points, style, arcDivisions, minDistance) { | |
| const vertices = []; | |
| const normals = []; | |
| const uvs = []; | |
| if (SVGLoader2.pointsToStrokeWithBuffers(points, style, arcDivisions, minDistance, vertices, normals, uvs) === 0) { | |
| return null; | |
| } | |
| const geometry = new THREE.BufferGeometry(); | |
| geometry.setAttribute("position", new THREE.Float32BufferAttribute(vertices, 3)); | |
| geometry.setAttribute("normal", new THREE.Float32BufferAttribute(normals, 3)); | |
| geometry.setAttribute("uv", new THREE.Float32BufferAttribute(uvs, 2)); | |
| return geometry; | |
| } | |
| static pointsToStrokeWithBuffers(points, style, arcDivisions, minDistance, vertices, normals, uvs, vertexOffset) { | |
| const tempV2_1 = new THREE.Vector2(); | |
| const tempV2_2 = new THREE.Vector2(); | |
| const tempV2_3 = new THREE.Vector2(); | |
| const tempV2_4 = new THREE.Vector2(); | |
| const tempV2_5 = new THREE.Vector2(); | |
| const tempV2_6 = new THREE.Vector2(); | |
| const tempV2_7 = new THREE.Vector2(); | |
| const lastPointL = new THREE.Vector2(); | |
| const lastPointR = new THREE.Vector2(); | |
| const point0L = new THREE.Vector2(); | |
| const point0R = new THREE.Vector2(); | |
| const currentPointL = new THREE.Vector2(); | |
| const currentPointR = new THREE.Vector2(); | |
| const nextPointL = new THREE.Vector2(); | |
| const nextPointR = new THREE.Vector2(); | |
| const innerPoint = new THREE.Vector2(); | |
| const outerPoint = new THREE.Vector2(); | |
| arcDivisions = arcDivisions !== void 0 ? arcDivisions : 12; | |
| minDistance = minDistance !== void 0 ? minDistance : 1e-3; | |
| vertexOffset = vertexOffset !== void 0 ? vertexOffset : 0; | |
| points = removeDuplicatedPoints(points); | |
| const numPoints = points.length; | |
| if (numPoints < 2) | |
| return 0; | |
| const isClosed = points[0].equals(points[numPoints - 1]); | |
| let currentPoint; | |
| let previousPoint = points[0]; | |
| let nextPoint; | |
| const strokeWidth2 = style.strokeWidth / 2; | |
| const deltaU = 1 / (numPoints - 1); | |
| let u0 = 0, u1; | |
| let innerSideModified; | |
| let joinIsOnLeftSide; | |
| let isMiter; | |
| let initialJoinIsOnLeftSide = false; | |
| let numVertices = 0; | |
| let currentCoordinate = vertexOffset * 3; | |
| let currentCoordinateUV = vertexOffset * 2; | |
| getNormal(points[0], points[1], tempV2_1).multiplyScalar(strokeWidth2); | |
| lastPointL.copy(points[0]).sub(tempV2_1); | |
| lastPointR.copy(points[0]).add(tempV2_1); | |
| point0L.copy(lastPointL); | |
| point0R.copy(lastPointR); | |
| for (let iPoint = 1; iPoint < numPoints; iPoint++) { | |
| currentPoint = points[iPoint]; | |
| if (iPoint === numPoints - 1) { | |
| if (isClosed) { | |
| nextPoint = points[1]; | |
| } else | |
| nextPoint = void 0; | |
| } else { | |
| nextPoint = points[iPoint + 1]; | |
| } | |
| const normal1 = tempV2_1; | |
| getNormal(previousPoint, currentPoint, normal1); | |
| tempV2_3.copy(normal1).multiplyScalar(strokeWidth2); | |
| currentPointL.copy(currentPoint).sub(tempV2_3); | |
| currentPointR.copy(currentPoint).add(tempV2_3); | |
| u1 = u0 + deltaU; | |
| innerSideModified = false; | |
| if (nextPoint !== void 0) { | |
| getNormal(currentPoint, nextPoint, tempV2_2); | |
| tempV2_3.copy(tempV2_2).multiplyScalar(strokeWidth2); | |
| nextPointL.copy(currentPoint).sub(tempV2_3); | |
| nextPointR.copy(currentPoint).add(tempV2_3); | |
| joinIsOnLeftSide = true; | |
| tempV2_3.subVectors(nextPoint, previousPoint); | |
| if (normal1.dot(tempV2_3) < 0) { | |
| joinIsOnLeftSide = false; | |
| } | |
| if (iPoint === 1) | |
| initialJoinIsOnLeftSide = joinIsOnLeftSide; | |
| tempV2_3.subVectors(nextPoint, currentPoint); | |
| tempV2_3.normalize(); | |
| const dot = Math.abs(normal1.dot(tempV2_3)); | |
| if (dot > Number.EPSILON) { | |
| const miterSide = strokeWidth2 / dot; | |
| tempV2_3.multiplyScalar(-miterSide); | |
| tempV2_4.subVectors(currentPoint, previousPoint); | |
| tempV2_5.copy(tempV2_4).setLength(miterSide).add(tempV2_3); | |
| innerPoint.copy(tempV2_5).negate(); | |
| const miterLength2 = tempV2_5.length(); | |
| const segmentLengthPrev = tempV2_4.length(); | |
| tempV2_4.divideScalar(segmentLengthPrev); | |
| tempV2_6.subVectors(nextPoint, currentPoint); | |
| const segmentLengthNext = tempV2_6.length(); | |
| tempV2_6.divideScalar(segmentLengthNext); | |
| if (tempV2_4.dot(innerPoint) < segmentLengthPrev && tempV2_6.dot(innerPoint) < segmentLengthNext) { | |
| innerSideModified = true; | |
| } | |
| outerPoint.copy(tempV2_5).add(currentPoint); | |
| innerPoint.add(currentPoint); | |
| isMiter = false; | |
| if (innerSideModified) { | |
| if (joinIsOnLeftSide) { | |
| nextPointR.copy(innerPoint); | |
| currentPointR.copy(innerPoint); | |
| } else { | |
| nextPointL.copy(innerPoint); | |
| currentPointL.copy(innerPoint); | |
| } | |
| } else { | |
| makeSegmentTriangles(); | |
| } | |
| switch (style.strokeLineJoin) { | |
| case "bevel": | |
| makeSegmentWithBevelJoin(joinIsOnLeftSide, innerSideModified, u1); | |
| break; | |
| case "round": | |
| createSegmentTrianglesWithMiddleSection(joinIsOnLeftSide, innerSideModified); | |
| if (joinIsOnLeftSide) { | |
| makeCircularSector(currentPoint, currentPointL, nextPointL, u1, 0); | |
| } else { | |
| makeCircularSector(currentPoint, nextPointR, currentPointR, u1, 1); | |
| } | |
| break; | |
| case "miter": | |
| case "miter-clip": | |
| default: | |
| const miterFraction = strokeWidth2 * style.strokeMiterLimit / miterLength2; | |
| if (miterFraction < 1) { | |
| if (style.strokeLineJoin !== "miter-clip") { | |
| makeSegmentWithBevelJoin(joinIsOnLeftSide, innerSideModified, u1); | |
| break; | |
| } else { | |
| createSegmentTrianglesWithMiddleSection(joinIsOnLeftSide, innerSideModified); | |
| if (joinIsOnLeftSide) { | |
| tempV2_6.subVectors(outerPoint, currentPointL).multiplyScalar(miterFraction).add(currentPointL); | |
| tempV2_7.subVectors(outerPoint, nextPointL).multiplyScalar(miterFraction).add(nextPointL); | |
| addVertex(currentPointL, u1, 0); | |
| addVertex(tempV2_6, u1, 0); | |
| addVertex(currentPoint, u1, 0.5); | |
| addVertex(currentPoint, u1, 0.5); | |
| addVertex(tempV2_6, u1, 0); | |
| addVertex(tempV2_7, u1, 0); | |
| addVertex(currentPoint, u1, 0.5); | |
| addVertex(tempV2_7, u1, 0); | |
| addVertex(nextPointL, u1, 0); | |
| } else { | |
| tempV2_6.subVectors(outerPoint, currentPointR).multiplyScalar(miterFraction).add(currentPointR); | |
| tempV2_7.subVectors(outerPoint, nextPointR).multiplyScalar(miterFraction).add(nextPointR); | |
| addVertex(currentPointR, u1, 1); | |
| addVertex(tempV2_6, u1, 1); | |
| addVertex(currentPoint, u1, 0.5); | |
| addVertex(currentPoint, u1, 0.5); | |
| addVertex(tempV2_6, u1, 1); | |
| addVertex(tempV2_7, u1, 1); | |
| addVertex(currentPoint, u1, 0.5); | |
| addVertex(tempV2_7, u1, 1); | |
| addVertex(nextPointR, u1, 1); | |
| } | |
| } | |
| } else { | |
| if (innerSideModified) { | |
| if (joinIsOnLeftSide) { | |
| addVertex(lastPointR, u0, 1); | |
| addVertex(lastPointL, u0, 0); | |
| addVertex(outerPoint, u1, 0); | |
| addVertex(lastPointR, u0, 1); | |
| addVertex(outerPoint, u1, 0); | |
| addVertex(innerPoint, u1, 1); | |
| } else { | |
| addVertex(lastPointR, u0, 1); | |
| addVertex(lastPointL, u0, 0); | |
| addVertex(outerPoint, u1, 1); | |
| addVertex(lastPointL, u0, 0); | |
| addVertex(innerPoint, u1, 0); | |
| addVertex(outerPoint, u1, 1); | |
| } | |
| if (joinIsOnLeftSide) { | |
| nextPointL.copy(outerPoint); | |
| } else { | |
| nextPointR.copy(outerPoint); | |
| } | |
| } else { | |
| if (joinIsOnLeftSide) { | |
| addVertex(currentPointL, u1, 0); | |
| addVertex(outerPoint, u1, 0); | |
| addVertex(currentPoint, u1, 0.5); | |
| addVertex(currentPoint, u1, 0.5); | |
| addVertex(outerPoint, u1, 0); | |
| addVertex(nextPointL, u1, 0); | |
| } else { | |
| addVertex(currentPointR, u1, 1); | |
| addVertex(outerPoint, u1, 1); | |
| addVertex(currentPoint, u1, 0.5); | |
| addVertex(currentPoint, u1, 0.5); | |
| addVertex(outerPoint, u1, 1); | |
| addVertex(nextPointR, u1, 1); | |
| } | |
| } | |
| isMiter = true; | |
| } | |
| break; | |
| } | |
| } else { | |
| makeSegmentTriangles(); | |
| } | |
| } else { | |
| makeSegmentTriangles(); | |
| } | |
| if (!isClosed && iPoint === numPoints - 1) { | |
| addCapGeometry(points[0], point0L, point0R, joinIsOnLeftSide, true, u0); | |
| } | |
| u0 = u1; | |
| previousPoint = currentPoint; | |
| lastPointL.copy(nextPointL); | |
| lastPointR.copy(nextPointR); | |
| } | |
| if (!isClosed) { | |
| addCapGeometry(currentPoint, currentPointL, currentPointR, joinIsOnLeftSide, false, u1); | |
| } else if (innerSideModified && vertices) { | |
| let lastOuter = outerPoint; | |
| let lastInner = innerPoint; | |
| if (initialJoinIsOnLeftSide !== joinIsOnLeftSide) { | |
| lastOuter = innerPoint; | |
| lastInner = outerPoint; | |
| } | |
| if (joinIsOnLeftSide) { | |
| if (isMiter || initialJoinIsOnLeftSide) { | |
| lastInner.toArray(vertices, 0 * 3); | |
| lastInner.toArray(vertices, 3 * 3); | |
| if (isMiter) { | |
| lastOuter.toArray(vertices, 1 * 3); | |
| } | |
| } | |
| } else { | |
| if (isMiter || !initialJoinIsOnLeftSide) { | |
| lastInner.toArray(vertices, 1 * 3); | |
| lastInner.toArray(vertices, 3 * 3); | |
| if (isMiter) { | |
| lastOuter.toArray(vertices, 0 * 3); | |
| } | |
| } | |
| } | |
| } | |
| return numVertices; | |
| function getNormal(p1, p2, result) { | |
| result.subVectors(p2, p1); | |
| return result.set(-result.y, result.x).normalize(); | |
| } | |
| function addVertex(position, u, v) { | |
| if (vertices) { | |
| vertices[currentCoordinate] = position.x; | |
| vertices[currentCoordinate + 1] = position.y; | |
| vertices[currentCoordinate + 2] = 0; | |
| if (normals) { | |
| normals[currentCoordinate] = 0; | |
| normals[currentCoordinate + 1] = 0; | |
| normals[currentCoordinate + 2] = 1; | |
| } | |
| currentCoordinate += 3; | |
| if (uvs) { | |
| uvs[currentCoordinateUV] = u; | |
| uvs[currentCoordinateUV + 1] = v; | |
| currentCoordinateUV += 2; | |
| } | |
| } | |
| numVertices += 3; | |
| } | |
| function makeCircularSector(center, p1, p2, u, v) { | |
| tempV2_1.copy(p1).sub(center).normalize(); | |
| tempV2_2.copy(p2).sub(center).normalize(); | |
| let angle = Math.PI; | |
| const dot = tempV2_1.dot(tempV2_2); | |
| if (Math.abs(dot) < 1) | |
| angle = Math.abs(Math.acos(dot)); | |
| angle /= arcDivisions; | |
| tempV2_3.copy(p1); | |
| for (let i = 0, il = arcDivisions - 1; i < il; i++) { | |
| tempV2_4.copy(tempV2_3).rotateAround(center, angle); | |
| addVertex(tempV2_3, u, v); | |
| addVertex(tempV2_4, u, v); | |
| addVertex(center, u, 0.5); | |
| tempV2_3.copy(tempV2_4); | |
| } | |
| addVertex(tempV2_4, u, v); | |
| addVertex(p2, u, v); | |
| addVertex(center, u, 0.5); | |
| } | |
| function makeSegmentTriangles() { | |
| addVertex(lastPointR, u0, 1); | |
| addVertex(lastPointL, u0, 0); | |
| addVertex(currentPointL, u1, 0); | |
| addVertex(lastPointR, u0, 1); | |
| addVertex(currentPointL, u1, 0); | |
| addVertex(currentPointR, u1, 1); | |
| } | |
| function makeSegmentWithBevelJoin(joinIsOnLeftSide2, innerSideModified2, u) { | |
| if (innerSideModified2) { | |
| if (joinIsOnLeftSide2) { | |
| addVertex(lastPointR, u0, 1); | |
| addVertex(lastPointL, u0, 0); | |
| addVertex(currentPointL, u1, 0); | |
| addVertex(lastPointR, u0, 1); | |
| addVertex(currentPointL, u1, 0); | |
| addVertex(innerPoint, u1, 1); | |
| addVertex(currentPointL, u, 0); | |
| addVertex(nextPointL, u, 0); | |
| addVertex(innerPoint, u, 0.5); | |
| } else { | |
| addVertex(lastPointR, u0, 1); | |
| addVertex(lastPointL, u0, 0); | |
| addVertex(currentPointR, u1, 1); | |
| addVertex(lastPointL, u0, 0); | |
| addVertex(innerPoint, u1, 0); | |
| addVertex(currentPointR, u1, 1); | |
| addVertex(currentPointR, u, 1); | |
| addVertex(innerPoint, u, 0); | |
| addVertex(nextPointR, u, 1); | |
| } | |
| } else { | |
| if (joinIsOnLeftSide2) { | |
| addVertex(currentPointL, u, 0); | |
| addVertex(nextPointL, u, 0); | |
| addVertex(currentPoint, u, 0.5); | |
| } else { | |
| addVertex(currentPointR, u, 1); | |
| addVertex(nextPointR, u, 0); | |
| addVertex(currentPoint, u, 0.5); | |
| } | |
| } | |
| } | |
| function createSegmentTrianglesWithMiddleSection(joinIsOnLeftSide2, innerSideModified2) { | |
| if (innerSideModified2) { | |
| if (joinIsOnLeftSide2) { | |
| addVertex(lastPointR, u0, 1); | |
| addVertex(lastPointL, u0, 0); | |
| addVertex(currentPointL, u1, 0); | |
| addVertex(lastPointR, u0, 1); | |
| addVertex(currentPointL, u1, 0); | |
| addVertex(innerPoint, u1, 1); | |
| addVertex(currentPointL, u0, 0); | |
| addVertex(currentPoint, u1, 0.5); | |
| addVertex(innerPoint, u1, 1); | |
| addVertex(currentPoint, u1, 0.5); | |
| addVertex(nextPointL, u0, 0); | |
| addVertex(innerPoint, u1, 1); | |
| } else { | |
| addVertex(lastPointR, u0, 1); | |
| addVertex(lastPointL, u0, 0); | |
| addVertex(currentPointR, u1, 1); | |
| addVertex(lastPointL, u0, 0); | |
| addVertex(innerPoint, u1, 0); | |
| addVertex(currentPointR, u1, 1); | |
| addVertex(currentPointR, u0, 1); | |
| addVertex(innerPoint, u1, 0); | |
| addVertex(currentPoint, u1, 0.5); | |
| addVertex(currentPoint, u1, 0.5); | |
| addVertex(innerPoint, u1, 0); | |
| addVertex(nextPointR, u0, 1); | |
| } | |
| } | |
| } | |
| function addCapGeometry(center, p1, p2, joinIsOnLeftSide2, start, u) { | |
| switch (style.strokeLineCap) { | |
| case "round": | |
| if (start) { | |
| makeCircularSector(center, p2, p1, u, 0.5); | |
| } else { | |
| makeCircularSector(center, p1, p2, u, 0.5); | |
| } | |
| break; | |
| case "square": | |
| if (start) { | |
| tempV2_1.subVectors(p1, center); | |
| tempV2_2.set(tempV2_1.y, -tempV2_1.x); | |
| tempV2_3.addVectors(tempV2_1, tempV2_2).add(center); | |
| tempV2_4.subVectors(tempV2_2, tempV2_1).add(center); | |
| if (joinIsOnLeftSide2) { | |
| tempV2_3.toArray(vertices, 1 * 3); | |
| tempV2_4.toArray(vertices, 0 * 3); | |
| tempV2_4.toArray(vertices, 3 * 3); | |
| } else { | |
| tempV2_3.toArray(vertices, 1 * 3); | |
| uvs[3 * 2 + 1] === 1 ? tempV2_4.toArray(vertices, 3 * 3) : tempV2_3.toArray(vertices, 3 * 3); | |
| tempV2_4.toArray(vertices, 0 * 3); | |
| } | |
| } else { | |
| tempV2_1.subVectors(p2, center); | |
| tempV2_2.set(tempV2_1.y, -tempV2_1.x); | |
| tempV2_3.addVectors(tempV2_1, tempV2_2).add(center); | |
| tempV2_4.subVectors(tempV2_2, tempV2_1).add(center); | |
| const vl = vertices.length; | |
| if (joinIsOnLeftSide2) { | |
| tempV2_3.toArray(vertices, vl - 1 * 3); | |
| tempV2_4.toArray(vertices, vl - 2 * 3); | |
| tempV2_4.toArray(vertices, vl - 4 * 3); | |
| } else { | |
| tempV2_4.toArray(vertices, vl - 2 * 3); | |
| tempV2_3.toArray(vertices, vl - 1 * 3); | |
| tempV2_4.toArray(vertices, vl - 4 * 3); | |
| } | |
| } | |
| break; | |
| } | |
| } | |
| function removeDuplicatedPoints(points2) { | |
| let dupPoints = false; | |
| for (let i = 1, n = points2.length - 1; i < n; i++) { | |
| if (points2[i].distanceTo(points2[i + 1]) < minDistance) { | |
| dupPoints = true; | |
| break; | |
| } | |
| } | |
| if (!dupPoints) | |
| return points2; | |
| const newPoints = []; | |
| newPoints.push(points2[0]); | |
| for (let i = 1, n = points2.length - 1; i < n; i++) { | |
| if (points2[i].distanceTo(points2[i + 1]) >= minDistance) { | |
| newPoints.push(points2[i]); | |
| } | |
| } | |
| newPoints.push(points2[points2.length - 1]); | |
| return newPoints; | |
| } | |
| } | |
| } | |
| return SVGLoader2; | |
| })(); | |
| exports.SVGLoader = SVGLoader; | |
| //# sourceMappingURL=SVGLoader.cjs.map | |
Xet Storage Details
- Size:
- 69.9 kB
- Xet hash:
- f36f8b2602821bdcd55f5aed38cc50a25efa574af31efafa741c1a2bc752f8f1
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.