Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da7e9f6a95
|
||
|
|
0b776a631c
|
||
|
|
17c91429f4
|
||
|
|
403daca793
|
||
|
|
90bfd67c71
|
||
|
|
3cb776566f
|
||
|
|
4ec8718cef
|
||
|
|
3308bec794
|
No files matched your search
@@ -1,3 +1,3 @@
|
||||
# Parachute-SVG-Generator
|
||||
|
||||
Generate the SVG outline of a parachute panel for an elliptical parachute.
|
||||
Generate the SVG outline of a parachute panel for an elliptical parachute
|
||||
Binary file not shown.
+9
-5
@@ -14,6 +14,11 @@
|
||||
<option value="cm">Centimetres</option>
|
||||
<option value="in">Inches</option>
|
||||
</select> <br>
|
||||
<label for="p">Paper size: </label>
|
||||
<select id="p">
|
||||
<option value="a3">A3</option>
|
||||
<option value="a4">A4</option>
|
||||
</select> <br>
|
||||
<label for="h">Height of parachute (h):</label>
|
||||
<input type="number" id="h" min="0"/><br>
|
||||
<label for="r">Radius of parachute (r):</label>
|
||||
@@ -22,11 +27,10 @@
|
||||
<input type="number" id="rs" /><br>
|
||||
<label for="np">Number of panels (n<sub>p</sub>): </label>
|
||||
<input type="number" id="np" min="3" ><br>
|
||||
<label for="steps">Number of steps: </label>
|
||||
<input type="number" id="steps" min="10" /><br>
|
||||
<button onclick="generatePanel()">Generate!</button>
|
||||
<button id="generate" >Generate!</button>
|
||||
<p id="len"></p>
|
||||
<div id="output"></div>
|
||||
<script src="index.js" lang="js"></script>
|
||||
<div id="output" style="display: none;"></div>
|
||||
<iframe id="pdf" style="width:100%;height:1000px"></iframe>
|
||||
<script src="src/main.js" lang="js" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,208 +0,0 @@
|
||||
let uInput = document.getElementById("u");
|
||||
let hInput = document.getElementById("h");
|
||||
let rInput = document.getElementById("r");
|
||||
let rsInput = document.getElementById("rs");
|
||||
let npInput = document.getElementById("np");
|
||||
let outputdiv = document.getElementById("output");
|
||||
let stepInput = document.getElementById("steps");
|
||||
let lenOutput = document.getElementById("len");
|
||||
|
||||
let height = 0;
|
||||
let radius = 0;
|
||||
let spillRadius = 0;
|
||||
let numberOfPanels = 0;
|
||||
|
||||
let multiplier = 1;
|
||||
let accuracy = 8;
|
||||
|
||||
let step = 0.001 * multiplier;
|
||||
// const offset = 3*10**-15 * multiplier;
|
||||
const offset = 4 * 10 ** -5 * multiplier;
|
||||
|
||||
let pointsP = [];
|
||||
let pointsQ = [];
|
||||
let distances = [];
|
||||
|
||||
let panelPoints = [];
|
||||
|
||||
let n = 0;
|
||||
let unit = "px";
|
||||
let strokeWidth = 1;
|
||||
|
||||
// maths
|
||||
function approx(n) {
|
||||
return Math.round(n * (10 ** accuracy)) / (10 ** accuracy);
|
||||
}
|
||||
|
||||
function f(x) {
|
||||
res = approx(height * Math.sqrt((1 - (Math.pow(x, 2) / Math.pow(radius, 2)))));
|
||||
console.log(`f ${x} ${res}`);
|
||||
return res;
|
||||
}
|
||||
|
||||
function fprime(x) {
|
||||
res = approx(-1 * ((Math.pow(height, 2) * x) / (Math.pow(radius, 2) * f(x))));
|
||||
console.log(`fprime ${x} ${res}`);
|
||||
return res;
|
||||
}
|
||||
|
||||
function g(x, P) {
|
||||
res = approx(fprime(x) * (x - P.x) + P.y);
|
||||
console.log(`g ${x} ${P.x} ${P.y} ${res}`);
|
||||
return res;
|
||||
}
|
||||
|
||||
function j(A, B) {
|
||||
res = approx(A.x * fprime(A.x) - B.x * fprime(B.x) - A.y + B.y) / (fprime(A.x) - fprime(B.x));
|
||||
console.log(`j ${A.x} ${A.y} ${B.x} ${B.y} ${res}`);
|
||||
return res;
|
||||
}
|
||||
|
||||
function d(A, B) {
|
||||
res = approx(Math.sqrt(Math.pow(A.x - B.x, 2) + Math.pow(A.y - B.y, 2)));
|
||||
console.log(`d ${A.x} ${A.y} ${B.x} ${B.y} ${res}`);
|
||||
return res;
|
||||
}
|
||||
|
||||
// logic
|
||||
function getValues() {
|
||||
pointsP = [];
|
||||
pointsQ = [];
|
||||
distances = [];
|
||||
panelPoints = [];
|
||||
height = /* /* Math.floor */(parseFloat(hInput.value) * multiplier);
|
||||
radius = /* Math.floor */(parseFloat(rInput.value) * multiplier);
|
||||
spillRadius = /* Math.floor */(parseFloat(rsInput.value) * multiplier);
|
||||
numberOfPanels = /* Math.floor */(parseFloat(npInput.value) * multiplier);
|
||||
step = radius / parseInt(stepInput.value);
|
||||
if (isNaN(spillRadius)) {
|
||||
spillRadius = /* Math.floor */(0.05 * radius);
|
||||
rsInput.value = 0.05 * radius / multiplier;
|
||||
}
|
||||
n = ((radius - spillRadius) / (step - offset)) - 1;
|
||||
console.log(`${height}, ${radius}, ${spillRadius}, ${step}, ${offset}`);
|
||||
}
|
||||
|
||||
function getPoints() {
|
||||
for (let x = spillRadius; x < radius; x += step - offset) {
|
||||
x = approx(x);
|
||||
pointsP.push({
|
||||
x: x,
|
||||
y: f(x)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getPointsOfTangentIntersection() {
|
||||
for (let i = 0; i < n; i++) {
|
||||
console.log(`${i}, ${pointsP[i]}, ${pointsP[i + 1]}`);
|
||||
pointsQ.push({
|
||||
x: j(pointsP[i], pointsP[i + 1]),
|
||||
y: g(j(pointsP[i], pointsP[i + 1]), pointsP[i + 1])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getDistancesBetweenIntersections() {
|
||||
for (let i = 0; i < n - 1; i++) {
|
||||
distances.push(d(pointsQ[i], pointsQ[i + 1]));
|
||||
}
|
||||
}
|
||||
|
||||
function generateCoordinates() {
|
||||
let Xi = 0;
|
||||
for (let i = 0; i < n - 2; i++) {
|
||||
Xi += distances[i];
|
||||
Yi = (Math.PI * pointsQ[i].x) / numberOfPanels;
|
||||
panelPoints.push({
|
||||
x: Xi,
|
||||
y: Yi
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function plotSVG() {
|
||||
let maxY = 0;
|
||||
let maxX = 0;
|
||||
let minX = 1000;
|
||||
panelPoints.forEach((point) => {
|
||||
if (maxY < point.y) {
|
||||
maxY = point.y;
|
||||
}
|
||||
if (minX > point.x) {
|
||||
minX = point.x;
|
||||
}
|
||||
if (maxX < point.x) {
|
||||
maxX = point.x;
|
||||
}
|
||||
})
|
||||
let pointsA = "";
|
||||
let pointsB = "";
|
||||
for (let i = 0; i < panelPoints.length; i++) {
|
||||
let point = panelPoints[i];
|
||||
pointsA += `${approx(point.x)},${(maxY) - approx(point.y)} `;
|
||||
pointsB +=`${approx(point.x)},${approx((point.y + maxY))} `;
|
||||
}
|
||||
outputdiv.innerHTML = `
|
||||
<svg width="297mm"
|
||||
height="210mm"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="layer1">
|
||||
<line
|
||||
x1="${approx(panelPoints[0].x)}${unit}"
|
||||
x2="${panelPoints[0].x}${unit}"
|
||||
y1="${maxY - approx(panelPoints[0].y)}${unit}"
|
||||
y2="${approx((panelPoints[0].y + maxY))}${unit}"
|
||||
fill="none"
|
||||
stroke="black"
|
||||
stroke-width="${strokeWidth}"
|
||||
/>
|
||||
<line
|
||||
x1="${approx(panelPoints[panelPoints.length - 1].x)}${unit}"
|
||||
x2="${panelPoints[panelPoints.length - 1].x}${unit}"
|
||||
y1="${maxY - approx(panelPoints[panelPoints.length - 1].y)}${unit}"
|
||||
y2="${approx((panelPoints[panelPoints.length - 1].y + maxY))}${unit}"
|
||||
fill="none"
|
||||
stroke="black"
|
||||
stroke-width="${strokeWidth}"
|
||||
/>
|
||||
<line
|
||||
x1="${approx(panelPoints[0].x)}${unit}"
|
||||
x2="${panelPoints[panelPoints.length - 1].x}${unit}"
|
||||
y1="${(maxY - approx(panelPoints[0].y) + approx((panelPoints[0].y + maxY))) / 2}${unit}"
|
||||
y2="${(maxY - approx(panelPoints[panelPoints.length - 1].y) + approx((panelPoints[panelPoints.length - 1].y + maxY))) / 2}${unit}"
|
||||
fill="none"
|
||||
stroke="black"
|
||||
stroke-width="${strokeWidth}"
|
||||
/>
|
||||
<polyline
|
||||
points="${pointsA}"
|
||||
fill="none"
|
||||
stroke="black"
|
||||
stroke-width="${strokeWidth}"
|
||||
/>
|
||||
<polyline
|
||||
points="${pointsB}"
|
||||
fill="none"
|
||||
stroke="black"
|
||||
stroke-width="${strokeWidth}"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
`;
|
||||
lenOutput.innerText = `Recommended length: ${approx(maxX - minX)} ${unit}`;
|
||||
}
|
||||
|
||||
|
||||
// main func
|
||||
function generatePanel() {
|
||||
unit=uInput.value;
|
||||
getValues();
|
||||
getPoints();
|
||||
getPointsOfTangentIntersection();
|
||||
getDistancesBetweenIntersections();
|
||||
generateCoordinates();
|
||||
plotSVG();
|
||||
}
|
||||
|
||||
|
||||
Generated
+820
-1
@@ -7,7 +7,826 @@
|
||||
"": {
|
||||
"name": "parachute-svg-generator",
|
||||
"version": "0.1.0",
|
||||
"license": "SEE LICENSE IN LICENSE"
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"dependencies": {
|
||||
"pdf-lib": "^1.17.1",
|
||||
"vite": "^8.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@oxc-project/types": {
|
||||
"version": "0.152.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.152.0.tgz",
|
||||
"integrity": "sha512-oM/5rLBm2tPkg0iBgkH/FOeR3PCDpY19GTgAZjMFM8h9WI9VW7cLgzp6nwtarYKmovavIQZ+Fe/RKX/8C8O/Rw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/oxc-project"
|
||||
}
|
||||
},
|
||||
"node_modules/@pdf-lib/standard-fonts": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",
|
||||
"integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pako": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@pdf-lib/upng": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz",
|
||||
"integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pako": "^1.0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-android-arm-eabi": {
|
||||
"version": "1.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.12.tgz",
|
||||
"integrity": "sha512-dB/a1214qKfHMXCpgqR4OZT+jS4kTyEXbQGJPqzobt5EwH5rX080pxE37alt3RzvR1bf1Yz/yGqRfrYAxuPw0A==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-android-arm64": {
|
||||
"version": "1.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.12.tgz",
|
||||
"integrity": "sha512-7KHFgQ5VJxIHcLlrwrc3Xbds7oTNQT7Pgi9gQCJKrd2VGab/UksIOYp6VD8MzCstGxOKMgNamPwUCfxPdP1OHg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-darwin-arm64": {
|
||||
"version": "1.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.12.tgz",
|
||||
"integrity": "sha512-3YIhqHD96nA5SaYNRBR16HnGv4oavZvXfD/ayHM+oYZ0WD/8lBAtf6zQua4kEyAvpqrluKXl0lnOBoiNby7x9w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-darwin-x64": {
|
||||
"version": "1.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.12.tgz",
|
||||
"integrity": "sha512-UuuJ35MFw4gmFOrE9pEqIV+K3syIKveph+Qc1/ljHZVdoDW4pz/JHR/eMVom+TZGl/5OOvGJOWaOCVt3ZfqhxA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-freebsd-x64": {
|
||||
"version": "1.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.12.tgz",
|
||||
"integrity": "sha512-uMvssit0a4W+/7D8CbHUvG719mH3R2jwXAlh/XcPvuHTE0g++LymF88DCGNX0HM2rBOn0xrzgXktIB6fLSJBTQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm-gnueabihf": {
|
||||
"version": "1.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.12.tgz",
|
||||
"integrity": "sha512-XcFu0R0xWnwzSf4IQgFH1rJIckPN1pLy2R+4r9IDB7Yfu/ys9cVqfa4pBrMHj7a3gl8mIR4nRNPg0e5IvEVs6g==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm64-gnu": {
|
||||
"version": "1.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.12.tgz",
|
||||
"integrity": "sha512-260UrKgn8tz39ak+SMDOirKzr7V04M9dWPw5llW00SwBivCZoWcRBKV1d8cXnRkUmSZA3BdiUmBHWk7734Ulpw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-arm64-musl": {
|
||||
"version": "1.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.12.tgz",
|
||||
"integrity": "sha512-5YK1I9SqDkbPgc1IA8BgDl34suqUS2q0KWnBrirm0E51YjOs6eo6dV6jbQfNE/argHRSvd0QUGgtpIoYx+WWpw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-ppc64-gnu": {
|
||||
"version": "1.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.12.tgz",
|
||||
"integrity": "sha512-Rkcrmp7eFRg74yL5fXEU91JEWbdEPLevWwGtXpmhbjlD1StScbWTmO94Bhly+Mo+ketKYkdmM1vNUKeWSlx8cQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-s390x-gnu": {
|
||||
"version": "1.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.12.tgz",
|
||||
"integrity": "sha512-qvK4DuAsQc2BSjlx+Xr+IzOIvvxbGZqxFwdWfG6F518Erj0GGISyQbJ6pIappnOxlNPzNHvo/L0BwB30GZ+zVw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-x64-gnu": {
|
||||
"version": "1.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.12.tgz",
|
||||
"integrity": "sha512-Q9uLBO53Xd4QIq1WOycVQyPP1O4HhraEV2qqb3uTrnVw6QZih9duY4vNXOivL1xoUS1/z+W8eF4NMfl2a8Sdjw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-linux-x64-musl": {
|
||||
"version": "1.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.12.tgz",
|
||||
"integrity": "sha512-3IBxWFMjbOZskDPKv8Lf9BCnahlKuHthWkYnyIxOH/QcJrFcS4EmcenthApkwr/5+nEqZlLzeYbxeMaX7A5u4g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-openharmony-arm64": {
|
||||
"version": "1.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.12.tgz",
|
||||
"integrity": "sha512-xtX61xg4LKPkPWilZU1ynKClz5Gj4bf74LML4r3eVLWumKnGjoEr1OSHQhMdbBDoYTi+yjrujvpZe2pUnqCrrA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-arm64-msvc": {
|
||||
"version": "1.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.12.tgz",
|
||||
"integrity": "sha512-At7fPB6PCaIjzgIhEZFxuT+BBFqiQibJDT4d3PhiR3f4E7bbMZF4aKblbFfEM3sETRDd1YiQx/+U/g/B/ou5Ew==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/binding-win32-x64-msvc": {
|
||||
"version": "1.2.12",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.12.tgz",
|
||||
"integrity": "sha512-WIw2haVKwjuYdXkHaoC0mF8Le71TuCBxjrdKqLbJGctbBABj+ClfmNvtbOnzpq3RokNo5+V1qhtSzJyXorsklQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@rolldown/pluginutils": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz",
|
||||
"integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/fdir": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"picomatch": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss": {
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz",
|
||||
"integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==",
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"lightningcss-android-arm64": "1.33.0",
|
||||
"lightningcss-darwin-arm64": "1.33.0",
|
||||
"lightningcss-darwin-x64": "1.33.0",
|
||||
"lightningcss-freebsd-x64": "1.33.0",
|
||||
"lightningcss-linux-arm-gnueabihf": "1.33.0",
|
||||
"lightningcss-linux-arm64-gnu": "1.33.0",
|
||||
"lightningcss-linux-arm64-musl": "1.33.0",
|
||||
"lightningcss-linux-x64-gnu": "1.33.0",
|
||||
"lightningcss-linux-x64-musl": "1.33.0",
|
||||
"lightningcss-win32-arm64-msvc": "1.33.0",
|
||||
"lightningcss-win32-x64-msvc": "1.33.0"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-android-arm64": {
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz",
|
||||
"integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-darwin-arm64": {
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz",
|
||||
"integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-darwin-x64": {
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz",
|
||||
"integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-freebsd-x64": {
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz",
|
||||
"integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-arm-gnueabihf": {
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz",
|
||||
"integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-arm64-gnu": {
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz",
|
||||
"integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-arm64-musl": {
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz",
|
||||
"integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-x64-gnu": {
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz",
|
||||
"integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-linux-x64-musl": {
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz",
|
||||
"integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-win32-arm64-msvc": {
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz",
|
||||
"integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lightningcss-win32-x64-msvc": {
|
||||
"version": "1.33.0",
|
||||
"resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz",
|
||||
"integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MPL-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.19",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz",
|
||||
"integrity": "sha512-Y2tUNy4ouw6tq5oDSKeQYGOyhkUBhNOcGV/02KC+6kd9eDGqdZd++mjMiIDilrBYvjEnCYvVtsuHCuP+okSfug==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||
"license": "(MIT AND Zlib)"
|
||||
},
|
||||
"node_modules/pdf-lib": {
|
||||
"version": "1.17.1",
|
||||
"resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz",
|
||||
"integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@pdf-lib/standard-fonts": "^1.0.0",
|
||||
"@pdf-lib/upng": "^1.0.1",
|
||||
"pako": "^1.0.11",
|
||||
"tslib": "^1.11.1"
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
|
||||
"integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.28",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz",
|
||||
"integrity": "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.18",
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/rolldown": {
|
||||
"version": "1.2.12",
|
||||
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.12.tgz",
|
||||
"integrity": "sha512-8wafseiaG80xmXSfqidUNqZcylTlhmPZZt+za2m+js2sFZ8dTNlhIOV2WcbIPx2hgwPBJpEUGFAMZ9bgBBLTSQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@oxc-project/types": "=0.152.0",
|
||||
"@rolldown/pluginutils": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"rolldown": "bin/cli.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rolldown/binding-android-arm-eabi": "1.2.12",
|
||||
"@rolldown/binding-android-arm64": "1.2.12",
|
||||
"@rolldown/binding-darwin-arm64": "1.2.12",
|
||||
"@rolldown/binding-darwin-x64": "1.2.12",
|
||||
"@rolldown/binding-freebsd-x64": "1.2.12",
|
||||
"@rolldown/binding-linux-arm-gnueabihf": "1.2.12",
|
||||
"@rolldown/binding-linux-arm64-gnu": "1.2.12",
|
||||
"@rolldown/binding-linux-arm64-musl": "1.2.12",
|
||||
"@rolldown/binding-linux-ppc64-gnu": "1.2.12",
|
||||
"@rolldown/binding-linux-s390x-gnu": "1.2.12",
|
||||
"@rolldown/binding-linux-x64-gnu": "1.2.12",
|
||||
"@rolldown/binding-linux-x64-musl": "1.2.12",
|
||||
"@rolldown/binding-openharmony-arm64": "1.2.12",
|
||||
"@rolldown/binding-win32-arm64-msvc": "1.2.12",
|
||||
"@rolldown/binding-win32-x64-msvc": "1.2.12"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz",
|
||||
"integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.17",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
|
||||
"integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fdir": "^6.5.0",
|
||||
"picomatch": "^4.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
|
||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-8.3.2.tgz",
|
||||
"integrity": "sha512-SQr1x6W5vVSbROg7vsyXIaxK9b0G7zsT68acdWWRmnBUsgDieLCRG+Rep9WdZgcposvv/GSnr4GUUBqB3vXq6w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lightningcss": "^1.33.0",
|
||||
"picomatch": "^4.0.7",
|
||||
"postcss": "^8.5.28",
|
||||
"rolldown": "~1.2.11",
|
||||
"tinyglobby": "^0.2.17"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.19.0 || >=22.12.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/vitejs/vite?sponsor=1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "~2.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": "^20.19.0 || >=22.12.0",
|
||||
"@vitejs/devtools": "^0.7.1",
|
||||
"esbuild": "^0.27.0 || ^0.28.0",
|
||||
"jiti": ">=1.21.0",
|
||||
"less": "^4.0.0",
|
||||
"sass": "^1.70.0",
|
||||
"sass-embedded": "^1.70.0",
|
||||
"stylus": ">=0.54.8",
|
||||
"sugarss": "^5.0.0",
|
||||
"terser": "^5.16.0",
|
||||
"tsx": "^4.8.1",
|
||||
"yaml": "^2.4.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitejs/devtools": {
|
||||
"optional": true
|
||||
},
|
||||
"esbuild": {
|
||||
"optional": true
|
||||
},
|
||||
"jiti": {
|
||||
"optional": true
|
||||
},
|
||||
"less": {
|
||||
"optional": true
|
||||
},
|
||||
"sass": {
|
||||
"optional": true
|
||||
},
|
||||
"sass-embedded": {
|
||||
"optional": true
|
||||
},
|
||||
"stylus": {
|
||||
"optional": true
|
||||
},
|
||||
"sugarss": {
|
||||
"optional": true
|
||||
},
|
||||
"terser": {
|
||||
"optional": true
|
||||
},
|
||||
"tsx": {
|
||||
"optional": true
|
||||
},
|
||||
"yaml": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-1
@@ -8,9 +8,15 @@
|
||||
},
|
||||
"license": "SEE LICENSE IN LICENSE",
|
||||
"author": "Oscar Giacalone",
|
||||
"type": "commonjs",
|
||||
"type": "module",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build; tar -cavf build.tar.gz dist",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"pdf-lib": "^1.17.1",
|
||||
"vite": "^8.3.2"
|
||||
}
|
||||
}
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
import * as maths from "./maths.js";
|
||||
import { createPdf } from "./print.js";
|
||||
|
||||
let uInput = document.getElementById("u");
|
||||
let hInput = document.getElementById("h");
|
||||
let rInput = document.getElementById("r");
|
||||
let rsInput = document.getElementById("rs");
|
||||
let npInput = document.getElementById("np");
|
||||
let outputdiv = document.getElementById("output");
|
||||
let stepInput = document.getElementById("steps");
|
||||
let paperInput = document.getElementById("p");
|
||||
|
||||
// parameters
|
||||
let height = 0;
|
||||
let radius = 0;
|
||||
let spillRadius = 0;
|
||||
let numberOfPanels = 0;
|
||||
let step = 0.001;
|
||||
const offset = 4 * 10 ** -5;
|
||||
|
||||
// temporary data
|
||||
let pointsP = [];
|
||||
let pointsQ = [];
|
||||
let distances = [];
|
||||
let panelPoints = [];
|
||||
|
||||
// print data
|
||||
let panelPaths = [];
|
||||
let svgSize = { width: 0, height: 0 };
|
||||
let scaleFactor = 1;
|
||||
let paperSize = "a3";
|
||||
|
||||
let n = 0;
|
||||
let unit = "px";
|
||||
|
||||
// logic
|
||||
function getValues() {
|
||||
pointsP = [];
|
||||
pointsQ = [];
|
||||
distances = [];
|
||||
panelPoints = [];
|
||||
height = (parseFloat(hInput.value) * 1);
|
||||
radius = (parseFloat(rInput.value) * 1);
|
||||
spillRadius = (parseFloat(rsInput.value) * 1);
|
||||
numberOfPanels = (parseFloat(npInput.value) * 1);
|
||||
step = radius / 1000;
|
||||
paperSize = paperInput.value;
|
||||
if (isNaN(spillRadius)) {
|
||||
spillRadius = (0.05 * radius);
|
||||
rsInput.value = 0.05 * radius ;
|
||||
}
|
||||
n = ((radius - spillRadius) / (step - offset)) - 1;
|
||||
}
|
||||
|
||||
function getPoints() {
|
||||
for (let x = spillRadius; x < radius; x += step - offset) {
|
||||
// round x to reduce oppourtunity for a NaN to occur from accuracy
|
||||
x = maths.approx(x);
|
||||
pointsP.push({
|
||||
x: x,
|
||||
y: maths.f(x, height, radius)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getPointsOfTangentIntersection() {
|
||||
for (let i = 0; i < n; i++) {
|
||||
pointsQ.push({
|
||||
x: maths.j(pointsP[i], pointsP[i + 1], height, radius),
|
||||
y: maths.g(maths.j(pointsP[i], pointsP[i + 1], height, radius), pointsP[i + 1], height, radius)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getDistancesBetweenIntersections() {
|
||||
for (let i = 0; i < n - 1; i++) {
|
||||
distances.push(maths.d(pointsQ[i], pointsQ[i + 1], height, radius));
|
||||
}
|
||||
}
|
||||
|
||||
function generateCoordinates() {
|
||||
let Xi = 0;
|
||||
for (let i = 0; i < n - 2; i++) {
|
||||
Xi += distances[i];
|
||||
let Yi = (Math.PI * pointsQ[i].x) / numberOfPanels;
|
||||
panelPoints.push({
|
||||
x: Xi,
|
||||
y: Yi
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function createPerimeterPath(maxY) {
|
||||
let path = `M `;
|
||||
|
||||
// first arc
|
||||
for (let i = 0; i < panelPoints.length; i++) {
|
||||
let point = panelPoints[i];
|
||||
path += `${maths.approx(point.x)},${maths.approx((maxY) - point.y)} `;
|
||||
}
|
||||
// second arc
|
||||
for (let i = panelPoints.length-1; i>=0;i--) {
|
||||
let point = panelPoints[i];
|
||||
path +=`${maths.approx(point.x)},${maths.approx((point.y + maxY))} `;
|
||||
}
|
||||
path += `${maths.approx(panelPoints[0].x)},${maths.approx((maxY) - panelPoints[0].y)}`;
|
||||
return path;
|
||||
}
|
||||
|
||||
function createCentrePath(maxY) {
|
||||
return `M ${maths.approx(panelPoints[0].x)},${(maxY - maths.approx(panelPoints[0].y) + maths.approx((panelPoints[0].y + maxY))) / 2} ${panelPoints[panelPoints.length - 1].x},${(maxY - maths.approx(panelPoints[panelPoints.length - 1].y) + maths.approx((panelPoints[panelPoints.length - 1].y + maxY))) / 2}`;
|
||||
}
|
||||
|
||||
function plotSVG() {
|
||||
let maxY = 0;
|
||||
let minY = 1000;
|
||||
let maxX = 0;
|
||||
let minX = 1000;
|
||||
panelPoints.forEach((point) => {
|
||||
if (maxY < point.y) {
|
||||
maxY = point.y;
|
||||
}
|
||||
if (minY > point.y) {
|
||||
minY = point.y;
|
||||
}
|
||||
if (minX > point.x) {
|
||||
minX = point.x;
|
||||
}
|
||||
if (maxX < point.x) {
|
||||
maxX = point.x;
|
||||
}
|
||||
});
|
||||
svgSize.width = panelPoints[panelPoints.length - 1].x - maths.approx(panelPoints[0].x);
|
||||
svgSize.height = (maxY - maths.approx(panelPoints[0].y) + maths.approx((panelPoints[0].y + maxY)));
|
||||
panelPaths[0] = createCentrePath(maxY);
|
||||
panelPaths[1] = createPerimeterPath(maxY);
|
||||
outputdiv.innerHTML = `
|
||||
<svg width="297mm"
|
||||
height="210mm"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="layer1">
|
||||
<path
|
||||
d="${panelPaths[0]}"
|
||||
fill="none"
|
||||
stroke="black"
|
||||
stroke-width="1"
|
||||
/>
|
||||
<path
|
||||
d="${panelPaths[1]}"
|
||||
fill="none"
|
||||
stroke="black"
|
||||
stroke-width="1"
|
||||
/>
|
||||
</g>
|
||||
</svg>`;
|
||||
}
|
||||
|
||||
function calculateScaleFactor(unit) {
|
||||
if (unit == "mm") {
|
||||
scaleFactor = 72/25.4;
|
||||
} else if (unit == "cm") {
|
||||
scaleFactor = 72/2.54;
|
||||
} else if (unit == "in") {
|
||||
scaleFactor = 72;
|
||||
} else if (unit == "px") {
|
||||
scaleFactor = 0.75;
|
||||
}
|
||||
}
|
||||
|
||||
// main func
|
||||
function generatePanel() {
|
||||
unit=uInput.value;
|
||||
getValues();
|
||||
getPoints();
|
||||
getPointsOfTangentIntersection();
|
||||
getDistancesBetweenIntersections();
|
||||
generateCoordinates();
|
||||
plotSVG();
|
||||
calculateScaleFactor(unit);
|
||||
createPdf(panelPaths, svgSize, scaleFactor, paperSize);
|
||||
}
|
||||
|
||||
document.getElementById("generate").addEventListener("click", () => generatePanel());
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { sanitiseArguments } from "./utils";
|
||||
|
||||
// decimal places
|
||||
let accuracy = 8;
|
||||
|
||||
export function approx(n) {
|
||||
return Math.round(n * (10 ** accuracy)) / (10 ** accuracy);
|
||||
}
|
||||
|
||||
// calculate y co-ordinate of point on ellipse at x
|
||||
// x: x co-ordinate
|
||||
// h: height of ellipse
|
||||
// r: radius of spill hole
|
||||
export function f(x, h, r) {
|
||||
sanitiseArguments(x,h,r)
|
||||
let res = approx(h * Math.sqrt((1 - (Math.pow(x, 2) / Math.pow(r, 2)))));
|
||||
return res;
|
||||
}
|
||||
|
||||
// calculate the gradient of the ellipse at a given point x
|
||||
// x: x co-ordinate of point
|
||||
// h: height of ellipse
|
||||
// r: radius of spill hole
|
||||
export function fprime(x, h, r) {
|
||||
sanitiseArguments(x,h,r)
|
||||
let res = approx(-1 * ((Math.pow(h, 2) * x) / (Math.pow(r, 2) * f(x, h, r))));
|
||||
return res;
|
||||
}
|
||||
|
||||
// calculate the y co-ordinate of the point on the tangent at P to an ellipse at x
|
||||
// x: x co-ordinate along tangent
|
||||
// P: (x,y) for the point on the ellipse the tangent is from
|
||||
// h: height of ellipse
|
||||
// r: radius of spill hole
|
||||
export function g(x, P, h, r) {
|
||||
sanitiseArguments(x,P.x, P.y,h,r)
|
||||
let res = approx(fprime(x, h, r) * (x - P.x) + P.y);
|
||||
return res;
|
||||
}
|
||||
|
||||
// find the intersection between two tangents of an ellipse
|
||||
// A: (x,y) for the point of the ellipse for the first tangent
|
||||
// B: (x,y) for the point of the ellipse for the second tangent
|
||||
// h: height of the ellipse
|
||||
// r: radius of the spill hole
|
||||
export function j(A, B, h, r) {
|
||||
sanitiseArguments(A.x, A.y, B.x, B.y, h,r)
|
||||
let res = approx(A.x * fprime(A.x, h, r) - B.x * fprime(B.x, h, r) - A.y + B.y) / (fprime(A.x, h, r) - fprime(B.x, h, r));
|
||||
return res;
|
||||
}
|
||||
|
||||
// find the distance between two points
|
||||
// A: (x,y) the first point
|
||||
// B: (x,y) the second point
|
||||
export function d(A, B) {
|
||||
sanitiseArguments(A.x, A.y, B.x, B.y)
|
||||
let res = approx(Math.sqrt(Math.pow(A.x - B.x, 2) + Math.pow(A.y - B.y, 2)));
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import { PDFDocument, degrees, rgb } from "pdf-lib";
|
||||
|
||||
export async function createPdf(paths, svgSize, scaleFactor, paperSize) {
|
||||
const pdfDocument = await PDFDocument.create();
|
||||
|
||||
// calculate number of pages needed
|
||||
|
||||
const margins = { x: 15, y: 15 };
|
||||
|
||||
// paper sizes in pt
|
||||
let sizes = {
|
||||
"a3": {
|
||||
x: 841.89-2*margins.x,
|
||||
y: 1190.55-2*margins.y,
|
||||
},
|
||||
"a4": {
|
||||
x: 595.28-2*margins.x,
|
||||
y: 841.89-2*margins.y,
|
||||
}
|
||||
}
|
||||
|
||||
let nPages = Math.ceil((svgSize.width *scaleFactor) / sizes[paperSize].y);
|
||||
let needsMarkers = false;
|
||||
if (1 < nPages) {
|
||||
needsMarkers = true;
|
||||
}
|
||||
|
||||
console.log(nPages);
|
||||
|
||||
// make enough pages
|
||||
for (let i = 0; i < nPages; i++) {
|
||||
let offset = i*(sizes[paperSize].y - 4 * margins.y);
|
||||
let page = pdfDocument.addPage();
|
||||
page.setSize(sizes[paperSize].x, sizes[paperSize].y);
|
||||
const { width, height } = page.getSize();
|
||||
|
||||
console.log(page.getSize());
|
||||
|
||||
const svgSettings = {
|
||||
x: (width + svgSize.height * scaleFactor)/2,
|
||||
y: height + offset - margins.y,
|
||||
rotate: degrees(-90),
|
||||
borderColor: rgb(0,0,0),
|
||||
borderWidth: 1,
|
||||
scale: scaleFactor
|
||||
};
|
||||
|
||||
page.moveTo(5, height - 5);
|
||||
// panel
|
||||
page.drawSvgPath(paths[0], svgSettings);
|
||||
page.drawSvgPath(paths[1], svgSettings);
|
||||
|
||||
// draw top alignmnet marker
|
||||
if (i != 0 && needsMarkers) {
|
||||
page.drawLine({
|
||||
start: { x: 0, y: height - 2 * margins.y },
|
||||
end: { x: width, y: height - 2 * margins.y },
|
||||
});
|
||||
}
|
||||
|
||||
// draw bottom alignment marker
|
||||
if (i != (nPages-1) && needsMarkers) {
|
||||
page.drawLine({
|
||||
start: { x: 0, y: 2 * margins.y },
|
||||
end: { x: width, y: 2 * margins.y },
|
||||
});
|
||||
}
|
||||
|
||||
// margins
|
||||
page.drawSvgPath(`M ${margins.x/2},${margins.y/2} ${margins.x/2},${height-margins.y/2} ${width-margins.x/2},${height-margins.y/2} ${width-margins.x/2},${margins.y/2} ${margins.x/2},${margins.y/2} Z`, {
|
||||
x:0,
|
||||
y:height,
|
||||
borderColor: rgb(1,1,1),
|
||||
borderWidth: margins.x
|
||||
});
|
||||
}
|
||||
|
||||
const pdfDataUri = await pdfDocument.saveAsBase64({ dataUri: true });
|
||||
document.getElementById("pdf").src = pdfDataUri;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export function sanitiseArguments(...args) {
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] == undefined || args[i] == NaN) {
|
||||
throw new Error(`bad argument: ${args}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import { defineConfig } from "vite";
|
||||
|
||||
export default defineConfig({
|
||||
base: ""
|
||||
});
|
||||
Reference in new issue
Block a user